diff --git a/servers/Azure.Mcp.Server/changelog-entries/1781549268619.yaml b/servers/Azure.Mcp.Server/changelog-entries/1781549268619.yaml
new file mode 100644
index 0000000000..594977e40d
--- /dev/null
+++ b/servers/Azure.Mcp.Server/changelog-entries/1781549268619.yaml
@@ -0,0 +1,3 @@
+changes:
+ - section: "Breaking Changes"
+ description: "Removed unused parameters from Azure Backup tools."
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Azure.Mcp.Tools.AzureBackup.csproj b/tools/Azure.Mcp.Tools.AzureBackup/src/Azure.Mcp.Tools.AzureBackup.csproj
index 9372dbb7da..3f2919954e 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Azure.Mcp.Tools.AzureBackup.csproj
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Azure.Mcp.Tools.AzureBackup.csproj
@@ -14,6 +14,5 @@
-
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Backup/BackupStatusCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Backup/BackupStatusCommand.cs
index 914fceac2b..bf3769d9bf 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Backup/BackupStatusCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Backup/BackupStatusCommand.cs
@@ -3,15 +3,13 @@
using System.Net;
using Azure.Mcp.Core.Commands.Subscription;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Backup;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Backup;
@@ -31,44 +29,23 @@ and the Azure region (location) where the resource exists.
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class BackupStatusCommand(ILogger logger, IAzureBackupService azureBackupService) : SubscriptionCommand()
+public sealed class BackupStatusCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : SubscriptionCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, BackupStatusOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.DatasourceId.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.Location.AsRequired());
- }
-
- protected override BackupStatusOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.DatasourceId = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.DatasourceId.Name);
- options.Location = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Location.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
context.Activity?.AddTag(AzureBackupTelemetryTags.OperationScope, "status-check");
try
{
var result = await _azureBackupService.GetBackupStatusAsync(
- options.DatasourceId!,
+ options.DatasourceId,
options.Subscription!,
- options.Location!,
+ options.Location,
options.Tenant,
options.RetryPolicy,
cancellationToken);
@@ -97,5 +74,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record BackupStatusCommandResult(BackupStatusResult Status);
+ public sealed record BackupStatusCommandResult(BackupStatusResult Status);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/BaseAzureBackupCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/BaseAzureBackupCommand.cs
index 929d38884f..b14dc32715 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/BaseAzureBackupCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/BaseAzureBackupCommand.cs
@@ -3,45 +3,24 @@
using System.Diagnostics.CodeAnalysis;
using Azure.Mcp.Core.Commands.Subscription;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Options;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands;
-public abstract class BaseAzureBackupCommand<
- [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] T>
- : SubscriptionCommand
- where T : BaseAzureBackupOptions, new()
+public abstract class BaseAzureBackupCommand<[DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions, TResult>(ISubscriptionResolver subscriptionResolver)
+ : SubscriptionCommand(subscriptionResolver) where TOptions : BaseAzureBackupOptions
{
- protected override void RegisterOptions(Command command)
+ public override void ValidateOptions(TOptions options, ValidationResult validationResult)
{
- base.RegisterOptions(command);
- command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.Vault.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.VaultType);
- command.Validators.Add(commandResult =>
- {
- if (commandResult.HasOptionResult(AzureBackupOptionDefinitions.VaultType.Name))
- {
- var value = commandResult.GetValue(AzureBackupOptionDefinitions.VaultType.Name);
- if (!string.IsNullOrEmpty(value) &&
- !value.Equals("rsv", StringComparison.OrdinalIgnoreCase) &&
- !value.Equals("dpp", StringComparison.OrdinalIgnoreCase))
- {
- commandResult.AddError("--vault-type must be 'rsv' (Recovery Services vault) or 'dpp' (Backup vault).");
- }
- }
- });
- }
+ base.ValidateOptions(options, validationResult);
- protected override T BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.ResourceGroup ??= parseResult.GetValueOrDefault(OptionDefinitions.Common.ResourceGroup.Name);
- options.Vault = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Vault.Name);
- options.VaultType = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.VaultType.Name);
- return options;
+ if (!string.IsNullOrEmpty(options.VaultType) &&
+ !options.VaultType.Equals("rsv", StringComparison.OrdinalIgnoreCase) &&
+ !options.VaultType.Equals("dpp", StringComparison.OrdinalIgnoreCase))
+ {
+ validationResult.Errors.Add("--vault-type must be 'rsv' (Recovery Services vault) or 'dpp' (Backup vault).");
+ }
}
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/BaseProtectedItemCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/BaseProtectedItemCommand.cs
deleted file mode 100644
index 5d5f9bc444..0000000000
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/BaseProtectedItemCommand.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-using System.Diagnostics.CodeAnalysis;
-using Azure.Mcp.Tools.AzureBackup.Options;
-using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
-using Microsoft.Mcp.Core.Models.Option;
-
-namespace Azure.Mcp.Tools.AzureBackup.Commands;
-
-public abstract class BaseProtectedItemCommand<
- [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] T>
- : BaseAzureBackupCommand
- where T : BaseProtectedItemOptions, new()
-{
- protected override void RegisterOptions(Command command)
- {
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.ProtectedItem.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.Container);
- }
-
- protected override T BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.ProtectedItem = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ProtectedItem.Name);
- options.Container = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Container.Name);
- return options;
- }
-}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/DisasterRecovery/DisasterRecoveryEnableCrrCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/DisasterRecovery/DisasterRecoveryEnableCrrCommand.cs
index adce3693e6..a1e96f95bb 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/DisasterRecovery/DisasterRecoveryEnableCrrCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/DisasterRecovery/DisasterRecoveryEnableCrrCommand.cs
@@ -2,14 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.DisasterRecovery;
@@ -24,28 +23,22 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.DisasterRecovery;
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class DisasterRecoveryEnableCrrCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class DisasterRecoveryEnableCrrCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
+ public override async Task ExecuteAsync(CommandContext context, BaseAzureBackupOptions options, CancellationToken cancellationToken)
{
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
try
{
var result = await _azureBackupService.ConfigureCrossRegionRestoreAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.VaultType,
options.Tenant,
@@ -81,5 +74,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record DisasterRecoveryEnableCrrCommandResult(OperationResult Result);
+ public sealed record DisasterRecoveryEnableCrrCommandResult(OperationResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceFindUnprotectedCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceFindUnprotectedCommand.cs
index e0531913b4..c4d0f07aa2 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceFindUnprotectedCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceFindUnprotectedCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using Azure.Mcp.Core.Commands.Subscription;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Governance;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Governance;
@@ -28,37 +26,14 @@ Scans the subscription to find Azure resources that are not currently protected
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class GovernanceFindUnprotectedCommand(ILogger logger, IAzureBackupService azureBackupService) : SubscriptionCommand()
+public sealed class GovernanceFindUnprotectedCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : SubscriptionCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, GovernanceFindUnprotectedOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.ResourceTypeFilter);
- command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsOptional());
- command.Options.Add(AzureBackupOptionDefinitions.TagFilter);
- }
-
- protected override GovernanceFindUnprotectedOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.ResourceTypeFilter = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ResourceTypeFilter.Name);
- options.ResourceGroup = parseResult.GetValueOrDefault(OptionDefinitions.Common.ResourceGroup.Name);
- options.TagFilter = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.TagFilter.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
context.Activity?.AddTag(AzureBackupTelemetryTags.OperationScope, "scan");
@@ -86,5 +61,5 @@ public override async Task ExecuteAsync(CommandContext context,
return context.Response;
}
- internal record GovernanceFindUnprotectedCommandResult(List Resources);
+ public sealed record GovernanceFindUnprotectedCommandResult(List Resources);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceImmutabilityCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceImmutabilityCommand.cs
index 756f504c6c..aada2ea21d 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceImmutabilityCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceImmutabilityCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Governance;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Governance;
@@ -28,47 +26,27 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.Governance;
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class GovernanceImmutabilityCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class GovernanceImmutabilityCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override void ValidateOptions(GovernanceImmutabilityOptions options, ValidationResult validationResult)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.ImmutabilityState.AsRequired());
- command.Validators.Add(commandResult =>
- {
- if (commandResult.HasOptionResult(AzureBackupOptionDefinitions.ImmutabilityState.Name))
- {
- var value = commandResult.GetValue(AzureBackupOptionDefinitions.ImmutabilityState.Name);
- if (!string.IsNullOrEmpty(value) &&
- !value.Equals("Disabled", StringComparison.OrdinalIgnoreCase) &&
- !value.Equals("Enabled", StringComparison.OrdinalIgnoreCase) &&
- !value.Equals("Locked", StringComparison.OrdinalIgnoreCase))
- {
- commandResult.AddError("--immutability-state must be 'Disabled', 'Enabled', or 'Locked'. Warning: 'Locked' is irreversible.");
- }
- }
- });
- }
-
- protected override GovernanceImmutabilityOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.ImmutabilityState = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ImmutabilityState.Name);
- return options;
- }
+ base.ValidateOptions(options, validationResult);
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
+ if (!string.IsNullOrEmpty(options.ImmutabilityState) &&
+ !options.ImmutabilityState.Equals("Disabled", StringComparison.OrdinalIgnoreCase) &&
+ !options.ImmutabilityState.Equals("Enabled", StringComparison.OrdinalIgnoreCase) &&
+ !options.ImmutabilityState.Equals("Locked", StringComparison.OrdinalIgnoreCase))
{
- return context.Response;
+ validationResult.Errors.Add("--immutability-state must be 'Disabled', 'Enabled', or 'Locked'. Warning: 'Locked' is irreversible.");
}
+ }
- var options = BindOptions(parseResult);
-
+ public override async Task ExecuteAsync(CommandContext context, GovernanceImmutabilityOptions options, CancellationToken cancellationToken)
+ {
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
@@ -109,5 +87,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record GovernanceImmutabilityCommandResult(OperationResult Result);
+ public sealed record GovernanceImmutabilityCommandResult(OperationResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceSoftDeleteCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceSoftDeleteCommand.cs
index 0bca4e2cc4..25e6ed795b 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceSoftDeleteCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Governance/GovernanceSoftDeleteCommand.cs
@@ -1,15 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Governance;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Governance;
@@ -27,71 +25,45 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.Governance;
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class GovernanceSoftDeleteCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class GovernanceSoftDeleteCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override void ValidateOptions(GovernanceSoftDeleteOptions options, ValidationResult validationResult)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.SoftDelete.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.SoftDeleteRetentionDays);
- command.Validators.Add(commandResult =>
- {
- if (commandResult.HasOptionResult(AzureBackupOptionDefinitions.SoftDelete.Name))
- {
- var value = commandResult.GetValue(AzureBackupOptionDefinitions.SoftDelete.Name);
- if (!string.IsNullOrEmpty(value) &&
- !value.Equals("AlwaysOn", StringComparison.OrdinalIgnoreCase) &&
- !value.Equals("On", StringComparison.OrdinalIgnoreCase) &&
- !value.Equals("Off", StringComparison.OrdinalIgnoreCase))
- {
- commandResult.AddError("--soft-delete must be 'AlwaysOn', 'On', or 'Off'.");
- }
- }
-
- if (commandResult.HasOptionResult(AzureBackupOptionDefinitions.SoftDeleteRetentionDays.Name))
- {
- var retentionValue = commandResult.GetValue(AzureBackupOptionDefinitions.SoftDeleteRetentionDays.Name);
- if (!string.IsNullOrEmpty(retentionValue))
- {
- if (!int.TryParse(retentionValue, out var retentionDays) || retentionDays < 14 || retentionDays > 180)
- {
- commandResult.AddError("--soft-delete-retention-days must be an integer between 14 and 180.");
- }
- }
- }
- });
- }
-
- protected override GovernanceSoftDeleteOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.SoftDeleteState = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.SoftDelete.Name);
- options.SoftDeleteRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.SoftDeleteRetentionDays.Name);
- return options;
- }
+ base.ValidateOptions(options, validationResult);
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
+ if (!string.IsNullOrEmpty(options.SoftDelete) &&
+ !options.SoftDelete.Equals("AlwaysOn", StringComparison.OrdinalIgnoreCase) &&
+ !options.SoftDelete.Equals("On", StringComparison.OrdinalIgnoreCase) &&
+ !options.SoftDelete.Equals("Off", StringComparison.OrdinalIgnoreCase))
{
- return context.Response;
+ validationResult.Errors.Add("--soft-delete must be 'AlwaysOn', 'On', or 'Off'.");
}
- var options = BindOptions(parseResult);
+ if (!string.IsNullOrEmpty(options.SoftDeleteRetentionDays) &&
+ (!int.TryParse(options.SoftDeleteRetentionDays, out var retentionDays)
+ || retentionDays < 14
+ || retentionDays > 180))
+ {
+ validationResult.Errors.Add("--soft-delete-retention-days must be an integer between 14 and 180.");
+ }
+ }
+ public override async Task ExecuteAsync(CommandContext context, GovernanceSoftDeleteOptions options, CancellationToken cancellationToken)
+ {
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
try
{
var result = await _azureBackupService.ConfigureSoftDeleteAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
- options.SoftDeleteState!,
+ options.SoftDelete,
options.VaultType,
options.SoftDeleteRetentionDays,
options.Tenant,
@@ -105,12 +77,12 @@ public override async Task ExecuteAsync(CommandContext context,
catch (Exception ex)
{
_logger.LogError(ex, "Error configuring soft delete. Vault: {Vault}, State: {SoftDeleteState}",
- options.Vault, options.SoftDeleteState);
+ options.Vault, options.SoftDelete);
HandleException(context, ex);
}
return context.Response;
}
- internal record GovernanceSoftDeleteCommandResult(OperationResult Result);
+ public sealed record GovernanceSoftDeleteCommandResult(OperationResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Job/JobGetCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Job/JobGetCommand.cs
index 7adb384775..b38a5a0d63 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Job/JobGetCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Job/JobGetCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Job;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Job;
@@ -33,33 +31,14 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.Job;
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class JobGetCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class JobGetCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, JobGetOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.Job.AsOptional());
- }
-
- protected override JobGetOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Job = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Job.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
context.Activity?.AddTag(AzureBackupTelemetryTags.OperationScope, string.IsNullOrEmpty(options.Job) ? "list" : "single");
@@ -69,8 +48,8 @@ public override async Task ExecuteAsync(CommandContext context,
if (!string.IsNullOrEmpty(options.Job))
{
var job = await _azureBackupService.GetJobAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.Job,
options.VaultType,
@@ -85,8 +64,8 @@ public override async Task ExecuteAsync(CommandContext context,
else
{
var jobs = await _azureBackupService.ListJobsAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.VaultType,
options.Tenant,
@@ -115,5 +94,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record JobGetCommandResult(List Jobs);
+ public sealed record JobGetCommandResult(List Jobs);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyCreateCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyCreateCommand.cs
index 28c30d2599..ca6651f8a6 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyCreateCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyCreateCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Policy;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Policy;
@@ -25,128 +23,14 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.Policy;
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class PolicyCreateCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class PolicyCreateCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, PolicyCreateOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.Policy.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.WorkloadType.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.DailyRetentionDays);
- // Common schedule flags (new in policy create overhaul; bound here, consumed by builders in a later commit).
- command.Options.Add(AzureBackupOptionDefinitions.TimeZone);
- command.Options.Add(AzureBackupOptionDefinitions.ScheduleFrequency);
- command.Options.Add(AzureBackupOptionDefinitions.ScheduleTimes);
- command.Options.Add(AzureBackupOptionDefinitions.ScheduleDaysOfWeek);
- command.Options.Add(AzureBackupOptionDefinitions.HourlyIntervalHours);
- command.Options.Add(AzureBackupOptionDefinitions.HourlyWindowStartTime);
- command.Options.Add(AzureBackupOptionDefinitions.HourlyWindowDurationHours);
- // Retention flags (new in policy create overhaul; bound here, consumed by builders in a later commit).
- command.Options.Add(AzureBackupOptionDefinitions.WeeklyRetentionWeeks);
- command.Options.Add(AzureBackupOptionDefinitions.WeeklyRetentionDaysOfWeek);
- command.Options.Add(AzureBackupOptionDefinitions.MonthlyRetentionMonths);
- command.Options.Add(AzureBackupOptionDefinitions.MonthlyRetentionWeekOfMonth);
- command.Options.Add(AzureBackupOptionDefinitions.MonthlyRetentionDaysOfWeek);
- command.Options.Add(AzureBackupOptionDefinitions.MonthlyRetentionDaysOfMonth);
- command.Options.Add(AzureBackupOptionDefinitions.YearlyRetentionYears);
- command.Options.Add(AzureBackupOptionDefinitions.YearlyRetentionMonths);
- command.Options.Add(AzureBackupOptionDefinitions.YearlyRetentionWeekOfMonth);
- command.Options.Add(AzureBackupOptionDefinitions.YearlyRetentionDaysOfWeek);
- command.Options.Add(AzureBackupOptionDefinitions.YearlyRetentionDaysOfMonth);
- command.Options.Add(AzureBackupOptionDefinitions.ArchiveTierAfterDays);
- command.Options.Add(AzureBackupOptionDefinitions.ArchiveTierMode);
- // RSV-VM only.
- command.Options.Add(AzureBackupOptionDefinitions.PolicySubType);
- command.Options.Add(AzureBackupOptionDefinitions.InstantRpRetentionDays);
- command.Options.Add(AzureBackupOptionDefinitions.InstantRpResourceGroup);
- command.Options.Add(AzureBackupOptionDefinitions.SnapshotConsistency);
- // RSV-VmWorkload (SQL / SAPHANA / SAPASE).
- command.Options.Add(AzureBackupOptionDefinitions.FullScheduleFrequency);
- command.Options.Add(AzureBackupOptionDefinitions.FullScheduleDaysOfWeek);
- command.Options.Add(AzureBackupOptionDefinitions.DifferentialScheduleDaysOfWeek);
- command.Options.Add(AzureBackupOptionDefinitions.DifferentialRetentionDays);
- command.Options.Add(AzureBackupOptionDefinitions.IncrementalScheduleDaysOfWeek);
- command.Options.Add(AzureBackupOptionDefinitions.IncrementalRetentionDays);
- command.Options.Add(AzureBackupOptionDefinitions.LogFrequencyMinutes);
- command.Options.Add(AzureBackupOptionDefinitions.LogRetentionDays);
- command.Options.Add(AzureBackupOptionDefinitions.IsCompression);
- command.Options.Add(AzureBackupOptionDefinitions.IsSqlCompression);
- // Stage 2 expansion.
- command.Options.Add(AzureBackupOptionDefinitions.SmartTier);
- command.Options.Add(AzureBackupOptionDefinitions.EnableSnapshotBackup);
- command.Options.Add(AzureBackupOptionDefinitions.SnapshotInstantRpRetentionDays);
- command.Options.Add(AzureBackupOptionDefinitions.SnapshotInstantRpResourceGroup);
- command.Options.Add(AzureBackupOptionDefinitions.EnableVaultTierCopy);
- command.Options.Add(AzureBackupOptionDefinitions.VaultTierCopyAfterDays);
- command.Options.Add(AzureBackupOptionDefinitions.BackupMode);
- command.Options.Add(AzureBackupOptionDefinitions.PitrRetentionDays);
- command.Options.Add(AzureBackupOptionDefinitions.PolicyTags);
- }
-
- protected override PolicyCreateOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Policy = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Policy.Name);
- options.WorkloadType = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.WorkloadType.Name);
- options.DailyRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.DailyRetentionDays.Name);
- options.TimeZone = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.TimeZone.Name);
- options.ScheduleFrequency = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ScheduleFrequency.Name);
- options.ScheduleTimes = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ScheduleTimes.Name);
- options.ScheduleDaysOfWeek = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ScheduleDaysOfWeek.Name);
- options.HourlyIntervalHours = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.HourlyIntervalHours.Name);
- options.HourlyWindowStartTime = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.HourlyWindowStartTime.Name);
- options.HourlyWindowDurationHours = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.HourlyWindowDurationHours.Name);
- options.WeeklyRetentionWeeks = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.WeeklyRetentionWeeks.Name);
- options.WeeklyRetentionDaysOfWeek = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.WeeklyRetentionDaysOfWeek.Name);
- options.MonthlyRetentionMonths = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.MonthlyRetentionMonths.Name);
- options.MonthlyRetentionWeekOfMonth = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.MonthlyRetentionWeekOfMonth.Name);
- options.MonthlyRetentionDaysOfWeek = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.MonthlyRetentionDaysOfWeek.Name);
- options.MonthlyRetentionDaysOfMonth = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.MonthlyRetentionDaysOfMonth.Name);
- options.YearlyRetentionYears = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.YearlyRetentionYears.Name);
- options.YearlyRetentionMonths = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.YearlyRetentionMonths.Name);
- options.YearlyRetentionWeekOfMonth = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.YearlyRetentionWeekOfMonth.Name);
- options.YearlyRetentionDaysOfWeek = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.YearlyRetentionDaysOfWeek.Name);
- options.YearlyRetentionDaysOfMonth = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.YearlyRetentionDaysOfMonth.Name);
- options.ArchiveTierAfterDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ArchiveTierAfterDays.Name);
- options.ArchiveTierMode = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ArchiveTierMode.Name);
- options.PolicySubType = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.PolicySubType.Name);
- options.InstantRpRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.InstantRpRetentionDays.Name);
- options.InstantRpResourceGroup = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.InstantRpResourceGroup.Name);
- options.SnapshotConsistency = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.SnapshotConsistency.Name);
- options.FullScheduleFrequency = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.FullScheduleFrequency.Name);
- options.FullScheduleDaysOfWeek = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.FullScheduleDaysOfWeek.Name);
- options.DifferentialScheduleDaysOfWeek = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.DifferentialScheduleDaysOfWeek.Name);
- options.DifferentialRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.DifferentialRetentionDays.Name);
- options.IncrementalScheduleDaysOfWeek = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.IncrementalScheduleDaysOfWeek.Name);
- options.IncrementalRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.IncrementalRetentionDays.Name);
- options.LogFrequencyMinutes = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.LogFrequencyMinutes.Name);
- options.LogRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.LogRetentionDays.Name);
- options.IsCompression = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.IsCompression.Name);
- options.IsSqlCompression = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.IsSqlCompression.Name);
- options.SmartTier = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.SmartTier.Name);
- options.EnableSnapshotBackup = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.EnableSnapshotBackup.Name);
- options.SnapshotInstantRpRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.SnapshotInstantRpRetentionDays.Name);
- options.SnapshotInstantRpResourceGroup = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.SnapshotInstantRpResourceGroup.Name);
- options.EnableVaultTierCopy = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.EnableVaultTierCopy.Name);
- options.VaultTierCopyAfterDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.VaultTierCopyAfterDays.Name);
- options.BackupMode = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.BackupMode.Name);
- options.PitrRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.PitrRetentionDays.Name);
- options.PolicyTags = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.PolicyTags.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultAndWorkloadTags(context.Activity, options.VaultType, options.WorkloadType);
@@ -162,8 +46,8 @@ public override async Task ExecuteAsync(CommandContext context,
{
var request = new Services.Policy.PolicyCreateRequest
{
- Policy = options.Policy!,
- WorkloadType = options.WorkloadType!,
+ Policy = options.Policy,
+ WorkloadType = options.WorkloadType,
DailyRetentionDays = options.DailyRetentionDays,
TimeZone = options.TimeZone,
ScheduleFrequency = options.ScheduleFrequency,
@@ -212,8 +96,8 @@ public override async Task ExecuteAsync(CommandContext context,
var result = await _azureBackupService.CreatePolicyAsync(
request,
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.VaultType,
options.Tenant,
@@ -247,5 +131,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record PolicyCreateCommandResult(OperationResult Result);
+ public sealed record PolicyCreateCommandResult(OperationResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyGetCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyGetCommand.cs
index 61af9f190f..2d5e60a325 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyGetCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyGetCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Policy;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Policy;
@@ -33,33 +31,14 @@ information about a single policy including datasource types and protected items
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class PolicyGetCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class PolicyGetCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, PolicyGetOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.Policy.AsOptional());
- }
-
- protected override PolicyGetOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Policy = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Policy.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
context.Activity?.AddTag(AzureBackupTelemetryTags.OperationScope, string.IsNullOrEmpty(options.Policy) ? "list" : "single");
@@ -69,8 +48,8 @@ public override async Task ExecuteAsync(CommandContext context,
if (!string.IsNullOrEmpty(options.Policy))
{
var policy = await _azureBackupService.GetPolicyAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.Policy,
options.VaultType,
@@ -115,5 +94,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record PolicyGetCommandResult(List Policies);
+ public sealed record PolicyGetCommandResult(List Policies);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyUpdateCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyUpdateCommand.cs
index ed7501734a..df5b5b1616 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyUpdateCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Policy/PolicyUpdateCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Policy;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Policy;
@@ -25,47 +23,24 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.Policy;
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class PolicyUpdateCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class PolicyUpdateCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, PolicyUpdateOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.Policy.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.ScheduleTime);
- command.Options.Add(AzureBackupOptionDefinitions.DailyRetentionDays);
- }
-
- protected override PolicyUpdateOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Policy = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Policy.Name);
- options.ScheduleTime = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ScheduleTime.Name);
- options.DailyRetentionDays = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.DailyRetentionDays.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
try
{
var result = await _azureBackupService.UpdatePolicyAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
- options.Policy!,
+ options.Policy,
options.VaultType,
options.ScheduleTime,
options.DailyRetentionDays,
@@ -105,5 +80,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetStatusCode(ex)
};
- internal record PolicyUpdateCommandResult(OperationResult Result);
+ public sealed record PolicyUpdateCommandResult(OperationResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectableItem/ProtectableItemListCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectableItem/ProtectableItemListCommand.cs
index 033524a89d..7aeef9e61b 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectableItem/ProtectableItemListCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectableItem/ProtectableItemListCommand.cs
@@ -1,13 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.ProtectableItem;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
namespace Azure.Mcp.Tools.AzureBackup.Commands.ProtectableItem;
@@ -29,58 +28,39 @@ such as SQL databases and SAP HANA databases discovered on registered VMs.
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class ProtectableItemListCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class ProtectableItemListCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override void ValidateOptions(ProtectableItemListOptions options, ValidationResult validationResult)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.WorkloadType);
- command.Options.Add(AzureBackupOptionDefinitions.Container);
- command.Validators.Add(commandResult =>
- {
- // NEW-4: reject unknown --workload-type at the command boundary so it surfaces
- // as a 400 ValidationError instead of leaking the inner ArgumentException
- // from the service layer as a 500.
- //
- // Read the value directly (no HasOptionResult gate) so whitespace-only inputs --
- // which System.CommandLine may report as "no result" -- still fail validation
- // here instead of slipping past and being rejected by the service layer.
- var value = commandResult.GetValueOrDefault(AzureBackupOptionDefinitions.WorkloadType.Name);
- if (value is not null && !WorkloadTypeNormalizer.IsSupported(value))
- {
- commandResult.AddError(WorkloadTypeNormalizer.FormatUnknownMessage(value));
- }
- });
- }
-
- protected override ProtectableItemListOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.WorkloadType = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.WorkloadType.Name);
- options.Container = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Container.Name);
- return options;
- }
+ base.ValidateOptions(options, validationResult);
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
+ // NEW-4: reject unknown --workload-type at the command boundary so it surfaces
+ // as a 400 ValidationError instead of leaking the inner ArgumentException
+ // from the service layer as a 500.
+ //
+ // Read the value directly (no HasOptionResult gate) so whitespace-only inputs --
+ // which System.CommandLine may report as "no result" -- still fail validation
+ // here instead of slipping past and being rejected by the service layer.
+ if (options.WorkloadType != null && !WorkloadTypeNormalizer.IsSupported(options.WorkloadType))
{
- return context.Response;
+ validationResult.Errors.Add(WorkloadTypeNormalizer.FormatUnknownMessage(options.WorkloadType));
}
+ }
- var options = BindOptions(parseResult);
-
+ public override async Task ExecuteAsync(CommandContext context, ProtectableItemListOptions options, CancellationToken cancellationToken)
+ {
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultAndWorkloadTags(context.Activity, options.VaultType ?? "rsv", options.WorkloadType);
try
{
var result = await _azureBackupService.ListProtectableItemsAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.WorkloadType,
options.Container,
@@ -109,5 +89,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record ProtectableItemListCommandResult(List Items);
+ public sealed record ProtectableItemListCommandResult(List Items);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemGetCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemGetCommand.cs
index 5a8d46d0cd..b678a891cd 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemGetCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemGetCommand.cs
@@ -2,14 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.ProtectedItem;
@@ -34,35 +33,14 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.ProtectedItem;
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class ProtectedItemGetCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class ProtectedItemGetCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, BaseProtectedItemOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.ProtectedItem.AsOptional());
- command.Options.Add(AzureBackupOptionDefinitions.Container);
- }
-
- protected override BaseProtectedItemOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.ProtectedItem = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ProtectedItem.Name);
- options.Container = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Container.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
context.Activity?.AddTag(AzureBackupTelemetryTags.OperationScope, string.IsNullOrEmpty(options.ProtectedItem) ? "list" : "single");
@@ -72,8 +50,8 @@ public override async Task ExecuteAsync(CommandContext context,
if (!string.IsNullOrEmpty(options.ProtectedItem))
{
var item = await _azureBackupService.GetProtectedItemAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.ProtectedItem,
options.VaultType,
@@ -89,8 +67,8 @@ public override async Task ExecuteAsync(CommandContext context,
else
{
var items = await _azureBackupService.ListProtectedItemsAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.VaultType,
options.Tenant,
@@ -127,5 +105,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record ProtectedItemGetCommandResult(List ProtectedItems);
+ public sealed record ProtectedItemGetCommandResult(List ProtectedItems);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemProtectCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemProtectCommand.cs
index b36d5a2ec9..b226399128 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemProtectCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemProtectCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.ProtectedItem;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.ProtectedItem;
@@ -34,49 +32,14 @@ For workloads (SQL/HANA): pass the protectable item name from 'protectableitem l
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class ProtectedItemProtectCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class ProtectedItemProtectCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, ProtectedItemProtectOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.DatasourceId.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.Policy.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.Container);
- command.Options.Add(AzureBackupOptionDefinitions.DatasourceType);
- command.Options.Add(AzureBackupOptionDefinitions.AksSnapshotResourceGroup);
- command.Options.Add(AzureBackupOptionDefinitions.AksIncludedNamespaces);
- command.Options.Add(AzureBackupOptionDefinitions.AksExcludedNamespaces);
- command.Options.Add(AzureBackupOptionDefinitions.AksLabelSelectors);
- command.Options.Add(AzureBackupOptionDefinitions.AksIncludeClusterScopeResources);
- }
-
- protected override ProtectedItemProtectOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.DatasourceId = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.DatasourceId.Name);
- options.Policy = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Policy.Name);
- options.Container = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Container.Name);
- options.DatasourceType = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.DatasourceType.Name);
- options.AksSnapshotResourceGroup = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.AksSnapshotResourceGroup.Name);
- options.AksIncludedNamespaces = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.AksIncludedNamespaces.Name);
- options.AksExcludedNamespaces = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.AksExcludedNamespaces.Name);
- options.AksLabelSelectors = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.AksLabelSelectors.Name);
- options.AksIncludeClusterScopeResources = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.AksIncludeClusterScopeResources.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
context.Activity?.AddTag(AzureBackupTelemetryTags.DatasourceType, AzureBackupTelemetryTags.NormalizeWorkloadType(options.DatasourceType));
@@ -84,8 +47,8 @@ public override async Task ExecuteAsync(CommandContext context,
try
{
var result = await _azureBackupService.ProtectItemAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.DatasourceId!,
options.Policy!,
@@ -126,5 +89,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record ProtectedItemProtectCommandResult(ProtectResult Result);
+ public sealed record ProtectedItemProtectCommandResult(ProtectResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemUndeleteCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemUndeleteCommand.cs
index 9636368a3e..cd9c973fd0 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemUndeleteCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/ProtectedItem/ProtectedItemUndeleteCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.ProtectedItem;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.ProtectedItem;
@@ -32,35 +30,14 @@ Use this when a backup or protected item was accidentally deleted and needs to b
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class ProtectedItemUndeleteCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class ProtectedItemUndeleteCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, ProtectedItemUndeleteOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.DatasourceId.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.Container);
- }
-
- protected override ProtectedItemUndeleteOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.DatasourceId = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.DatasourceId.Name);
- options.Container = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.Container.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
@@ -105,5 +82,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record ProtectedItemUndeleteCommandResult(OperationResult Result);
+ public sealed record ProtectedItemUndeleteCommandResult(OperationResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/RecoveryPoint/RecoveryPointGetCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/RecoveryPoint/RecoveryPointGetCommand.cs
index eb3ba29441..bc52306bdb 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/RecoveryPoint/RecoveryPointGetCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/RecoveryPoint/RecoveryPointGetCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.RecoveryPoint;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.RecoveryPoint;
@@ -33,33 +31,14 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.RecoveryPoint;
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class RecoveryPointGetCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseProtectedItemCommand()
+public sealed class RecoveryPointGetCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, RecoveryPointGetOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.RecoveryPoint.AsOptional());
- }
-
- protected override RecoveryPointGetOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.RecoveryPoint = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.RecoveryPoint.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
context.Activity?.AddTag(AzureBackupTelemetryTags.OperationScope, string.IsNullOrEmpty(options.RecoveryPoint) ? "list" : "single");
@@ -69,10 +48,10 @@ public override async Task ExecuteAsync(CommandContext context,
if (!string.IsNullOrEmpty(options.RecoveryPoint))
{
var rp = await _azureBackupService.GetRecoveryPointAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
- options.ProtectedItem!,
+ options.ProtectedItem,
options.RecoveryPoint,
options.VaultType,
options.Container,
@@ -87,10 +66,10 @@ public override async Task ExecuteAsync(CommandContext context,
else
{
var points = await _azureBackupService.ListRecoveryPointsAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
- options.ProtectedItem!,
+ options.ProtectedItem,
options.VaultType,
options.Container,
options.Tenant,
@@ -124,5 +103,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetErrorMessage(ex)
};
- internal record RecoveryPointGetCommandResult(List RecoveryPoints);
+ public sealed record RecoveryPointGetCommandResult(List RecoveryPoints);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Security/SecurityConfigureEncryptionCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Security/SecurityConfigureEncryptionCommand.cs
index e62ec41046..03d1a53a21 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Security/SecurityConfigureEncryptionCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Security/SecurityConfigureEncryptionCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Security;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Security;
@@ -31,87 +29,59 @@ identity must have the Key Vault Crypto Service Encryption User role on the Key
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class SecurityConfigureEncryptionCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class SecurityConfigureEncryptionCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
private string? _lastVaultType;
- protected override void RegisterOptions(Command command)
+ public override void ValidateOptions(SecurityConfigureEncryptionOptions options, ValidationResult validationResult)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.KeyVaultUri);
- command.Options.Add(AzureBackupOptionDefinitions.KeyNameOption);
- command.Options.Add(AzureBackupOptionDefinitions.KeyVersion);
- command.Options.Add(AzureBackupOptionDefinitions.IdentityType.AsRequired());
- command.Options.Add(AzureBackupOptionDefinitions.UserAssignedIdentityId);
+ base.ValidateOptions(options, validationResult);
- command.Validators.Add(commandResult =>
+ if (!string.IsNullOrEmpty(options.IdentityType) &&
+ !options.IdentityType.Equals("SystemAssigned", StringComparison.OrdinalIgnoreCase) &&
+ !options.IdentityType.Equals("UserAssigned", StringComparison.OrdinalIgnoreCase))
{
- var identityType = commandResult.GetValue(AzureBackupOptionDefinitions.IdentityType.Name);
- if (!string.IsNullOrEmpty(identityType) &&
- !identityType.Equals("SystemAssigned", StringComparison.OrdinalIgnoreCase) &&
- !identityType.Equals("UserAssigned", StringComparison.OrdinalIgnoreCase))
+ validationResult.Errors.Add("--identity-type must be 'SystemAssigned' or 'UserAssigned' for CMK encryption.");
+ }
+
+ if (string.Equals(options.IdentityType, "UserAssigned", StringComparison.OrdinalIgnoreCase))
+ {
+ if (string.IsNullOrEmpty(options.UserAssignedIdentityId))
{
- commandResult.AddError("--identity-type must be 'SystemAssigned' or 'UserAssigned' for CMK encryption.");
+ validationResult.Errors.Add("--user-assigned-identity-id is required when --identity-type is 'UserAssigned'.");
}
-
- if (string.Equals(identityType, "UserAssigned", StringComparison.OrdinalIgnoreCase))
+ else if (!options.UserAssignedIdentityId.StartsWith("/subscriptions/", StringComparison.OrdinalIgnoreCase))
{
- var uaId = commandResult.GetValue(AzureBackupOptionDefinitions.UserAssignedIdentityId.Name);
- if (string.IsNullOrEmpty(uaId))
- {
- commandResult.AddError("--user-assigned-identity-id is required when --identity-type is 'UserAssigned'.");
- }
- else if (!uaId.StartsWith("/subscriptions/", StringComparison.OrdinalIgnoreCase))
- {
- commandResult.AddError("--user-assigned-identity-id must be a valid ARM resource ID starting with '/subscriptions/'.");
- }
+ validationResult.Errors.Add("--user-assigned-identity-id must be a valid ARM resource ID starting with '/subscriptions/'.");
}
+ }
- var keyVaultUri = commandResult.GetValue(AzureBackupOptionDefinitions.KeyVaultUri.Name);
- if (!string.IsNullOrEmpty(keyVaultUri))
+ if (!string.IsNullOrEmpty(options.KeyVaultUri))
+ {
+ if (!Uri.TryCreate(options.KeyVaultUri, UriKind.Absolute, out var uri))
+ {
+ validationResult.Errors.Add("--key-vault-uri must be a valid URI (e.g., 'https://kv-name.vault.azure.net/').");
+ }
+ else
{
- if (!Uri.TryCreate(keyVaultUri, UriKind.Absolute, out var uri))
+ if (!string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
- commandResult.AddError("--key-vault-uri must be a valid URI (e.g., 'https://kv-name.vault.azure.net/').");
+ validationResult.Errors.Add("--key-vault-uri must use HTTPS (e.g., 'https://kv-name.vault.azure.net/').");
}
- else
- {
- if (!string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
- {
- commandResult.AddError("--key-vault-uri must use HTTPS (e.g., 'https://kv-name.vault.azure.net/').");
- }
- if (uri.AbsolutePath != "/" && !string.IsNullOrEmpty(uri.AbsolutePath.TrimEnd('/')))
- {
- commandResult.AddError("--key-vault-uri must be the Key Vault base URI without path segments (e.g., 'https://kv-name.vault.azure.net/'). Do not include '/keys/...' in the URI.");
- }
+ if (uri.AbsolutePath != "/" && !string.IsNullOrEmpty(uri.AbsolutePath.TrimEnd('/')))
+ {
+ validationResult.Errors.Add("--key-vault-uri must be the Key Vault base URI without path segments (e.g., 'https://kv-name.vault.azure.net/'). Do not include '/keys/...' in the URI.");
}
}
- });
- }
-
- protected override SecurityConfigureEncryptionOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.KeyVaultUri = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.KeyVaultUri.Name);
- options.KeyName = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.KeyNameOption.Name);
- options.KeyVersion = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.KeyVersion.Name);
- options.IdentityType = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.IdentityType.Name);
- options.UserAssignedIdentityId = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.UserAssignedIdentityId.Name);
- return options;
+ }
}
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
+ public override async Task ExecuteAsync(CommandContext context, SecurityConfigureEncryptionOptions options, CancellationToken cancellationToken)
{
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
_lastVaultType = options.VaultType;
@@ -119,12 +89,12 @@ public override async Task ExecuteAsync(CommandContext context,
try
{
var result = await _azureBackupService.ConfigureEncryptionAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
- options.KeyVaultUri!,
- options.KeyName!,
- options.IdentityType!,
+ options.KeyVaultUri,
+ options.KeyName,
+ options.IdentityType,
options.KeyVersion,
options.UserAssignedIdentityId,
options.VaultType,
@@ -171,5 +141,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetStatusCode(ex)
};
- internal record SecurityConfigureEncryptionCommandResult(OperationResult Result);
+ public sealed record SecurityConfigureEncryptionCommandResult(OperationResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Security/SecurityConfigureMuaCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Security/SecurityConfigureMuaCommand.cs
index db1c6d3fc9..1e3e32e97d 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Security/SecurityConfigureMuaCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Security/SecurityConfigureMuaCommand.cs
@@ -2,13 +2,12 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Security;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Security;
@@ -30,45 +29,25 @@ namespace Azure.Mcp.Tools.AzureBackup.Commands.Security;
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class SecurityConfigureMuaCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class SecurityConfigureMuaCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly IAzureBackupService _azureBackupService = azureBackupService;
- protected override void RegisterOptions(Command command)
+ public override void ValidateOptions(SecurityConfigureMuaOptions options, ValidationResult validationResult)
{
- base.RegisterOptions(command);
- command.Options.Add(AzureBackupOptionDefinitions.ResourceGuardId);
- command.Validators.Add(commandResult =>
- {
- if (commandResult.HasOptionResult(AzureBackupOptionDefinitions.ResourceGuardId.Name))
- {
- var value = commandResult.GetValue(AzureBackupOptionDefinitions.ResourceGuardId.Name);
- if (!string.IsNullOrEmpty(value) &&
- !value.StartsWith("/subscriptions/", StringComparison.OrdinalIgnoreCase))
- {
- commandResult.AddError("--resource-guard-id must be a valid ARM resource ID starting with '/subscriptions/'.");
- }
- }
- });
- }
+ base.ValidateOptions(options, validationResult);
- protected override SecurityConfigureMuaOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.ResourceGuardId = parseResult.GetValueOrDefault(AzureBackupOptionDefinitions.ResourceGuardId.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
+ if (!string.IsNullOrEmpty(options.ResourceGuardId) &&
+ !options.ResourceGuardId.StartsWith("/subscriptions/", StringComparison.OrdinalIgnoreCase))
{
- return context.Response;
+ validationResult.Errors.Add("--resource-guard-id must be a valid ARM resource ID starting with '/subscriptions/'.");
}
+ }
- var options = BindOptions(parseResult);
-
+ public override async Task ExecuteAsync(CommandContext context, SecurityConfigureMuaOptions options, CancellationToken cancellationToken)
+ {
AzureBackupTelemetryTags.AddSubscriptionTag(context.Activity, options.Subscription);
AzureBackupTelemetryTags.AddVaultTags(context.Activity, options.VaultType);
@@ -79,8 +58,8 @@ public override async Task ExecuteAsync(CommandContext context,
if (!string.IsNullOrEmpty(options.ResourceGuardId))
{
result = await _azureBackupService.ConfigureMultiUserAuthorizationAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.ResourceGuardId,
options.VaultType,
@@ -91,8 +70,8 @@ public override async Task ExecuteAsync(CommandContext context,
else
{
result = await _azureBackupService.DisableMultiUserAuthorizationAsync(
- options.Vault!,
- options.ResourceGroup!,
+ options.Vault,
+ options.ResourceGroup,
options.Subscription!,
options.VaultType,
options.Tenant,
@@ -138,5 +117,5 @@ public override async Task ExecuteAsync(CommandContext context,
_ => base.GetStatusCode(ex)
};
- internal record SecurityConfigureMuaCommandResult(OperationResult Result);
+ public sealed record SecurityConfigureMuaCommandResult(OperationResult Result);
}
diff --git a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Vault/VaultCreateCommand.cs b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Vault/VaultCreateCommand.cs
index c9a3ed0155..26800d8742 100644
--- a/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Vault/VaultCreateCommand.cs
+++ b/tools/Azure.Mcp.Tools.AzureBackup/src/Commands/Vault/VaultCreateCommand.cs
@@ -2,15 +2,13 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.AzureBackup.Models;
-using Azure.Mcp.Tools.AzureBackup.Options;
using Azure.Mcp.Tools.AzureBackup.Options.Vault;
using Azure.Mcp.Tools.AzureBackup.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.AzureBackup.Commands.Vault;
@@ -32,69 +30,34 @@ vault details.
ReadOnly = false,
Secret = false,
LocalRequired = false)]
-public sealed class VaultCreateCommand(ILogger logger, IAzureBackupService azureBackupService) : BaseAzureBackupCommand()
+public sealed class VaultCreateCommand(ILogger logger, IAzureBackupService azureBackupService, ISubscriptionResolver subscriptionResolver)
+ : BaseAzureBackupCommand