From 6ea3afc6503f80539c3541e442c9215adb96e25c Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:06:18 -0400 Subject: [PATCH 1/2] Migrate Sql tools to new tool design --- .../changelog-entries/1782421569874.yaml | 3 + .../src/Commands/BaseDatabaseCommand.cs | 28 --- .../src/Commands/BaseElasticPoolCommand.cs | 15 -- .../src/Commands/BaseSqlCommand.cs | 34 ---- .../Database/DatabaseCreateCommand.cs | 57 ++---- .../Database/DatabaseDeleteCommand.cs | 26 +-- .../Commands/Database/DatabaseGetCommand.cs | 49 ++--- .../Database/DatabaseRenameCommand.cs | 41 +--- .../Database/DatabaseUpdateCommand.cs | 53 +---- .../ElasticPool/ElasticPoolListCommand.cs | 23 +-- .../EntraAdmin/EntraAdminListCommand.cs | 27 +-- .../FirewallRule/FirewallRuleCreateCommand.cs | 96 +++------ .../FirewallRule/FirewallRuleDeleteCommand.cs | 52 ++--- .../FirewallRule/FirewallRuleListCommand.cs | 25 +-- .../Commands/Server/ServerCreateCommand.cs | 63 ++---- .../Commands/Server/ServerDeleteCommand.cs | 44 +--- .../src/Commands/Server/ServerGetCommand.cs | 38 +--- tools/Azure.Mcp.Tools.Sql/src/GlobalUsings.cs | 4 - .../src/Options/BaseDatabaseOptions.cs | 12 -- .../src/Options/BaseElasticPoolOptions.cs | 6 - .../src/Options/BaseSqlOptions.cs | 14 +- .../Options/Database/DatabaseCreateOptions.cs | 26 ++- .../Options/Database/DatabaseDeleteOptions.cs | 9 +- .../Options/Database/DatabaseGetOptions.cs | 9 +- .../Options/Database/DatabaseRenameOptions.cs | 14 +- .../Options/Database/DatabaseUpdateOptions.cs | 26 ++- .../ElasticPool/ElasticPoolListOptions.cs | 8 +- .../EntraAdmin/EntraAdminListOptions.cs | 7 +- .../FirewallRule/FirewallRuleCreateOptions.cs | 19 +- .../FirewallRule/FirewallRuleDeleteOptions.cs | 11 +- .../FirewallRule/FirewallRuleListOptions.cs | 7 +- .../src/Options/Server/ServerCreateOptions.cs | 23 ++- .../src/Options/Server/ServerDeleteOptions.cs | 8 +- .../src/Options/Server/ServerGetOptions.cs | 8 +- .../src/Options/SqlOptionDefinitions.cs | 188 ------------------ .../src/Options/SqlOptionDescriptions.cs | 19 ++ .../src/Services/Models/SqlDatabaseData.cs | 53 +++-- .../Services/Models/SqlDatabaseProperties.cs | 63 +++--- .../Services/Models/SqlFirewallRuleData.cs | 45 ++--- .../Models/SqlFirewallRuleProperties.cs | 27 ++- .../Models/SqlServerAadAdministratorData.cs | 45 ++--- .../SqlServerAadAdministratorProperties.cs | 37 ++-- .../src/Services/Models/SqlSku.cs | 29 ++- .../src/Services/SqlService.cs | 10 +- .../Database/DatabaseCreateCommandTests.cs | 4 +- .../Database/DatabaseDeleteCommandTests.cs | 4 +- .../Database/DatabaseGetCommandTests.cs | 4 +- .../Database/DatabaseRenameCommandTests.cs | 9 +- .../Database/DatabaseUpdateCommandTests.cs | 9 +- .../ElasticPoolListCommandTests.cs | 4 +- .../EntraAdmin/EntraAdminListCommandTests.cs | 4 +- .../FirewallRuleCreateCommandTests.cs | 4 +- .../FirewallRuleDeleteCommandTests.cs | 4 +- .../FirewallRuleListCommandTests.cs | 4 +- .../Server/ServerCreateCommandTests.cs | 4 +- .../Server/ServerDeleteCommandTests.cs | 4 +- .../Server/ServerGetCommandTests.cs | 4 +- 57 files changed, 495 insertions(+), 968 deletions(-) create mode 100644 servers/Azure.Mcp.Server/changelog-entries/1782421569874.yaml delete mode 100644 tools/Azure.Mcp.Tools.Sql/src/Commands/BaseDatabaseCommand.cs delete mode 100644 tools/Azure.Mcp.Tools.Sql/src/Commands/BaseElasticPoolCommand.cs delete mode 100644 tools/Azure.Mcp.Tools.Sql/src/Commands/BaseSqlCommand.cs delete mode 100644 tools/Azure.Mcp.Tools.Sql/src/GlobalUsings.cs delete mode 100644 tools/Azure.Mcp.Tools.Sql/src/Options/BaseDatabaseOptions.cs delete mode 100644 tools/Azure.Mcp.Tools.Sql/src/Options/BaseElasticPoolOptions.cs delete mode 100644 tools/Azure.Mcp.Tools.Sql/src/Options/SqlOptionDefinitions.cs create mode 100644 tools/Azure.Mcp.Tools.Sql/src/Options/SqlOptionDescriptions.cs diff --git a/servers/Azure.Mcp.Server/changelog-entries/1782421569874.yaml b/servers/Azure.Mcp.Server/changelog-entries/1782421569874.yaml new file mode 100644 index 0000000000..8d27cbdfee --- /dev/null +++ b/servers/Azure.Mcp.Server/changelog-entries/1782421569874.yaml @@ -0,0 +1,3 @@ +changes: + - section: "Breaking Changes" + description: "Removed unused parameters from Sql tools." diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseDatabaseCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseDatabaseCommand.cs deleted file mode 100644 index 8b26e41637..0000000000 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseDatabaseCommand.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Diagnostics.CodeAnalysis; -using Azure.Mcp.Tools.Sql.Options; -using Microsoft.Extensions.Logging; -using Microsoft.Mcp.Core.Commands; -using Microsoft.Mcp.Core.Extensions; - -namespace Azure.Mcp.Tools.Sql.Commands; - -public abstract class BaseDatabaseCommand< - [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions>(ILogger> logger) - : BaseSqlCommand(logger) where TOptions : BaseDatabaseOptions, new() -{ - protected override void RegisterOptions(Command command) - { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.Database); - } - - protected override TOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.Database = parseResult.GetValueOrDefault(SqlOptionDefinitions.Database.Name); - return options; - } -} diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseElasticPoolCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseElasticPoolCommand.cs deleted file mode 100644 index f1c0a4bc13..0000000000 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseElasticPoolCommand.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Diagnostics.CodeAnalysis; -using Azure.Mcp.Tools.Sql.Options; -using Microsoft.Extensions.Logging; -using Microsoft.Mcp.Core.Commands; - -namespace Azure.Mcp.Tools.Sql.Commands; - -public abstract class BaseElasticPoolCommand< - [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions>(ILogger> logger) - : BaseSqlCommand(logger) where TOptions : BaseElasticPoolOptions, new() -{ -} diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseSqlCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseSqlCommand.cs deleted file mode 100644 index 4aa0ee18b0..0000000000 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/BaseSqlCommand.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Diagnostics.CodeAnalysis; -using Azure.Mcp.Core.Commands.Subscription; -using Azure.Mcp.Tools.Sql.Options; -using Microsoft.Extensions.Logging; -using Microsoft.Mcp.Core.Commands; -using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; - -namespace Azure.Mcp.Tools.Sql.Commands; - -public abstract class BaseSqlCommand< - [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions>(ILogger> logger) - : SubscriptionCommand where TOptions : BaseSqlOptions, new() -{ - protected readonly ILogger> _logger = logger; - - protected override void RegisterOptions(Command command) - { - base.RegisterOptions(command); - command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsRequired()); - command.Options.Add(SqlOptionDefinitions.Server); - } - - protected override TOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.ResourceGroup ??= parseResult.GetValueOrDefault(OptionDefinitions.Common.ResourceGroup.Name); - options.Server = parseResult.GetValueOrDefault(SqlOptionDefinitions.Server.Name); - return options; - } -} diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseCreateCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseCreateCommand.cs index c2127ce312..8f0a84ff72 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseCreateCommand.cs @@ -2,13 +2,13 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; -using Azure.Mcp.Tools.Sql.Options; using Azure.Mcp.Tools.Sql.Options.Database; using Azure.Mcp.Tools.Sql.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.Sql.Commands.Database; @@ -18,8 +18,8 @@ namespace Azure.Mcp.Tools.Sql.Commands.Database; Name = "create", Title = "Create SQL Database", Description = """ - Create a new Azure SQL Database on an existing SQL Server. This command creates a database with configurable - performance tiers, size limits, and other settings. Equivalent to 'az sql db create'. + Create a new Azure SQL Database on an existing SQL Server with configurable performance tiers, size limits, + and other settings. Equivalent to 'az sql db create'. Returns the newly created database information including configuration details. """, Destructive = true, @@ -28,53 +28,20 @@ Returns the newly created database information including configuration details. ReadOnly = false, Secret = false, LocalRequired = false)] -public sealed class DatabaseCreateCommand(ISqlService sqlService, ILogger logger) - : BaseDatabaseCommand(logger) +public sealed class DatabaseCreateCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, DatabaseCreateOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.SkuNameOption); - command.Options.Add(SqlOptionDefinitions.SkuTierOption); - command.Options.Add(SqlOptionDefinitions.SkuCapacityOption); - command.Options.Add(SqlOptionDefinitions.CollationOption); - command.Options.Add(SqlOptionDefinitions.MaxSizeBytesOption); - command.Options.Add(SqlOptionDefinitions.ElasticPoolNameOption); - command.Options.Add(SqlOptionDefinitions.ZoneRedundantOption); - command.Options.Add(SqlOptionDefinitions.ReadScaleOption); - } - - protected override DatabaseCreateOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.SkuName = parseResult.GetValueOrDefault(SqlOptionDefinitions.SkuNameOption.Name); - options.SkuTier = parseResult.GetValueOrDefault(SqlOptionDefinitions.SkuTierOption.Name); - options.SkuCapacity = parseResult.GetValueOrDefault(SqlOptionDefinitions.SkuCapacityOption.Name); - options.Collation = parseResult.GetValueOrDefault(SqlOptionDefinitions.CollationOption.Name); - options.MaxSizeBytes = parseResult.GetValueOrDefault(SqlOptionDefinitions.MaxSizeBytesOption.Name); - options.ElasticPoolName = parseResult.GetValueOrDefault(SqlOptionDefinitions.ElasticPoolNameOption.Name); - options.ZoneRedundant = parseResult.GetValueOrDefault(SqlOptionDefinitions.ZoneRedundantOption.Name); - options.ReadScale = parseResult.GetValueOrDefault(SqlOptionDefinitions.ReadScaleOption.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); - try { var database = await _sqlService.CreateDatabaseAsync( - options.Server!, - options.Database!, - options.ResourceGroup!, + options.Server, + options.Database, + options.ResourceGroup, options.Subscription!, options.SkuName, options.SkuTier, @@ -114,5 +81,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - internal record DatabaseCreateResult(SqlDatabase Database); + public sealed record DatabaseCreateResult(SqlDatabase Database); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseDeleteCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseDeleteCommand.cs index 989aba270f..84b1b9d4c7 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseDeleteCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseDeleteCommand.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Options.Database; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Extensions.Logging; @@ -14,33 +16,27 @@ namespace Azure.Mcp.Tools.Sql.Commands.Database; Id = "c4ef0375-0df9-445c-b8ae-2542e9612425", Name = "delete", Title = "Delete SQL Database", - Description = "Deletes a database from an Azure SQL Server.This idempotent operation removes the specified database from the server, returning Deleted = false if the database doesn't exist or Deleted = true if successfully removed.", + Description = "Deletes a database from an Azure SQL Server. This idempotent operation removes the specified database from the server, returning Deleted = false if the database doesn't exist or Deleted = true if successfully removed.", Destructive = true, Idempotent = true, OpenWorld = false, ReadOnly = false, Secret = false, LocalRequired = false)] -public sealed class DatabaseDeleteCommand(ISqlService sqlService, ILogger logger) - : BaseDatabaseCommand(logger) +public sealed class DatabaseDeleteCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) + public override async Task ExecuteAsync(CommandContext context, DatabaseDeleteOptions options, CancellationToken cancellationToken) { - if (!Validate(parseResult.CommandResult, context.Response).IsValid) - { - return context.Response; - } - - var options = BindOptions(parseResult); - try { var deleted = await _sqlService.DeleteDatabaseAsync( - options.Server!, - options.Database!, - options.ResourceGroup!, + options.Server, + options.Database, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -68,6 +64,6 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - internal record DatabaseDeleteResult(bool Deleted, string DatabaseName); + public sealed record DatabaseDeleteResult(bool Deleted, string DatabaseName); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseGetCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseGetCommand.cs index d55ee35c90..25a1237ea5 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseGetCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseGetCommand.cs @@ -2,15 +2,14 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; -using Azure.Mcp.Tools.Sql.Options; using Azure.Mcp.Tools.Sql.Options.Database; using Azure.Mcp.Tools.Sql.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.Sql.Commands.Database; @@ -20,10 +19,9 @@ namespace Azure.Mcp.Tools.Sql.Commands.Database; Title = "Get SQL Database", Description = """ Show, get, or list Azure SQL databases in a SQL Server. Shows details for a specific Azure SQL database - by name, or lists all Azure SQL databases in the specified SQL Server. Use to show or retrieve Azure SQL - database information. Equivalent to 'az sql db show' (show one Azure SQL database) or 'az sql db list' - (list all Azure SQL databases in a server). Returns database information including configuration details - and current status. + by name, or lists all Azure SQL databases in the specified SQL Server. Equivalent to 'az sql db show' + (show one Azure SQL database) or 'az sql db list' (list all Azure SQL databases in a server). + Returns database information including configuration details and current status. """, Destructive = false, Idempotent = true, @@ -31,41 +29,22 @@ and current status. ReadOnly = true, Secret = false, LocalRequired = false)] -public sealed class DatabaseGetCommand(ISqlService sqlService, ILogger logger) - : BaseSqlCommand(logger) +public sealed class DatabaseGetCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, DatabaseGetOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.Database.AsOptional()); - } - - protected override DatabaseGetOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.Database = parseResult.GetValueOrDefault(SqlOptionDefinitions.Database.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); - try { if (!string.IsNullOrEmpty(options.Database)) { var database = await _sqlService.GetDatabaseAsync( - options.Server!, + options.Server, options.Database, - options.ResourceGroup!, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -77,8 +56,8 @@ public override async Task ExecuteAsync(CommandContext context, else { var databases = await _sqlService.ListDatabasesAsync( - options.Server!, - options.ResourceGroup!, + options.Server, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -109,5 +88,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - internal record DatabaseGetListResult(List Databases); + public sealed record DatabaseGetListResult(List Databases); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseRenameCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseRenameCommand.cs index d1e5463eb5..2f55e80675 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseRenameCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseRenameCommand.cs @@ -2,13 +2,13 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; -using Azure.Mcp.Tools.Sql.Options; using Azure.Mcp.Tools.Sql.Options.Database; using Azure.Mcp.Tools.Sql.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.Sql.Commands.Database; @@ -28,40 +28,21 @@ namespace Azure.Mcp.Tools.Sql.Commands.Database; ReadOnly = false, Secret = false, LocalRequired = false)] -public sealed class DatabaseRenameCommand(ISqlService sqlService, ILogger logger) - : BaseDatabaseCommand(logger) +public sealed class DatabaseRenameCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, DatabaseRenameOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.NewDatabaseNameOption); - } - - protected override DatabaseRenameOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.NewDatabaseName = parseResult.GetValueOrDefault(SqlOptionDefinitions.NewDatabaseNameOption.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); - try { var database = await _sqlService.RenameDatabaseAsync( - options.Server!, - options.Database!, - options.NewDatabaseName!, - options.ResourceGroup!, + options.Server, + options.Database, + options.NewDatabaseName, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -93,5 +74,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - internal record DatabaseRenameResult(SqlDatabase Database); + public sealed record DatabaseRenameResult(SqlDatabase Database); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseUpdateCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseUpdateCommand.cs index cd293a260b..af83a90ecf 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseUpdateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseUpdateCommand.cs @@ -2,13 +2,13 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; -using Azure.Mcp.Tools.Sql.Options; using Azure.Mcp.Tools.Sql.Options.Database; using Azure.Mcp.Tools.Sql.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.Sql.Commands.Database; @@ -29,53 +29,20 @@ Returns the updated database configuration including applied scaling changes. ReadOnly = false, Secret = false, LocalRequired = false)] -public sealed class DatabaseUpdateCommand(ISqlService sqlService, ILogger logger) - : BaseDatabaseCommand(logger) +public sealed class DatabaseUpdateCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, DatabaseUpdateOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.SkuNameOption); - command.Options.Add(SqlOptionDefinitions.SkuTierOption); - command.Options.Add(SqlOptionDefinitions.SkuCapacityOption); - command.Options.Add(SqlOptionDefinitions.CollationOption); - command.Options.Add(SqlOptionDefinitions.MaxSizeBytesOption); - command.Options.Add(SqlOptionDefinitions.ElasticPoolNameOption); - command.Options.Add(SqlOptionDefinitions.ZoneRedundantOption); - command.Options.Add(SqlOptionDefinitions.ReadScaleOption); - } - - protected override DatabaseUpdateOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.SkuName = parseResult.GetValueOrDefault(SqlOptionDefinitions.SkuNameOption.Name); - options.SkuTier = parseResult.GetValueOrDefault(SqlOptionDefinitions.SkuTierOption.Name); - options.SkuCapacity = parseResult.GetValueOrDefault(SqlOptionDefinitions.SkuCapacityOption.Name); - options.Collation = parseResult.GetValueOrDefault(SqlOptionDefinitions.CollationOption.Name); - options.MaxSizeBytes = parseResult.GetValueOrDefault(SqlOptionDefinitions.MaxSizeBytesOption.Name); - options.ElasticPoolName = parseResult.GetValueOrDefault(SqlOptionDefinitions.ElasticPoolNameOption.Name); - options.ZoneRedundant = parseResult.GetValueOrDefault(SqlOptionDefinitions.ZoneRedundantOption.Name); - options.ReadScale = parseResult.GetValueOrDefault(SqlOptionDefinitions.ReadScaleOption.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); - try { var database = await _sqlService.UpdateDatabaseAsync( - options.Server!, - options.Database!, - options.ResourceGroup!, + options.Server, + options.Database, + options.ResourceGroup, options.Subscription!, options.SkuName, options.SkuTier, @@ -113,5 +80,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - internal record DatabaseUpdateResult(SqlDatabase Database); + public sealed record DatabaseUpdateResult(SqlDatabase Database); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/ElasticPool/ElasticPoolListCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/ElasticPool/ElasticPoolListCommand.cs index 3c5082cb1d..775e6672cc 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/ElasticPool/ElasticPoolListCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/ElasticPool/ElasticPoolListCommand.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.ElasticPool; using Azure.Mcp.Tools.Sql.Services; @@ -19,7 +21,6 @@ namespace Azure.Mcp.Tools.Sql.Commands.ElasticPool; Lists all SQL elastic pools in an Azure SQL Server with their SKU, capacity, state, and database limits. Use when you need to: view elastic pool inventory, check pool utilization, compare pool configurations, or find available pools for database placement. - Requires: subscription ID, resource group name, server name. Returns: JSON array of elastic pools with complete configuration details. Equivalent to 'az sql elastic-pool list'. """, @@ -29,25 +30,19 @@ Equivalent to 'az sql elastic-pool list'. ReadOnly = true, Secret = false, LocalRequired = false)] -public sealed class ElasticPoolListCommand(ISqlService sqlService, ILogger logger) - : BaseElasticPoolCommand(logger) +public sealed class ElasticPoolListCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) + public override async Task ExecuteAsync(CommandContext context, ElasticPoolListOptions options, CancellationToken cancellationToken) { - if (!Validate(parseResult.CommandResult, context.Response).IsValid) - { - return context.Response; - } - - var options = BindOptions(parseResult); - try { var elasticPools = await _sqlService.GetElasticPoolsAsync( - options.Server!, - options.ResourceGroup!, + options.Server, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -75,5 +70,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - internal record ElasticPoolListResult(List ElasticPools); + public sealed record ElasticPoolListResult(List ElasticPools); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/EntraAdmin/EntraAdminListCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/EntraAdmin/EntraAdminListCommand.cs index ec34155c81..fbeb508901 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/EntraAdmin/EntraAdminListCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/EntraAdmin/EntraAdminListCommand.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.EntraAdmin; using Azure.Mcp.Tools.Sql.Services; @@ -16,9 +18,8 @@ namespace Azure.Mcp.Tools.Sql.Commands.EntraAdmin; Name = "list", Title = "List SQL Server Entra ID Administrators", Description = """ - Gets a list of Microsoft Entra ID administrators for a SQL server. This command retrieves all - Entra ID administrators configured for the specified SQL server, including their display names, object IDs, - and tenant information. Returns an array of Entra ID administrator objects with their properties. + Gets a list of all Microsoft Entra ID administrators for a SQL server, including their display names, + object IDs, and tenant information. Returns an array of Entra ID administrator objects with their properties. """, Destructive = false, Idempotent = true, @@ -26,25 +27,19 @@ and tenant information. Returns an array of Entra ID administrator objects with ReadOnly = true, Secret = false, LocalRequired = false)] -public sealed class EntraAdminListCommand(ISqlService sqlService, ILogger logger) - : BaseSqlCommand(logger) +public sealed class EntraAdminListCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) + public override async Task ExecuteAsync(CommandContext context, EntraAdminListOptions options, CancellationToken cancellationToken) { - if (!Validate(parseResult.CommandResult, context.Response).IsValid) - { - return context.Response; - } - - var options = BindOptions(parseResult); - try { var administrators = await _sqlService.GetEntraAdministratorsAsync( - options.Server!, - options.ResourceGroup!, + options.Server, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -72,5 +67,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - internal record EntraAdminListResult(List Administrators); + public sealed record EntraAdminListResult(List Administrators); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleCreateCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleCreateCommand.cs index 76afd8e747..f49196c379 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleCreateCommand.cs @@ -3,13 +3,13 @@ using System.Net; using System.Net.Sockets; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; -using Azure.Mcp.Tools.Sql.Options; using Azure.Mcp.Tools.Sql.Options.FirewallRule; using Azure.Mcp.Tools.Sql.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.Sql.Commands.FirewallRule; @@ -30,52 +30,36 @@ the created firewall rule with its properties. ReadOnly = false, Secret = false, LocalRequired = false)] -public sealed class FirewallRuleCreateCommand(ISqlService sqlService, ILogger logger) - : BaseSqlCommand(logger) +public sealed class FirewallRuleCreateCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override void ValidateOptions(FirewallRuleCreateOptions options, ValidationResult validationResult) { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.FirewallRuleNameOption); - command.Options.Add(SqlOptionDefinitions.StartIpAddressOption); - command.Options.Add(SqlOptionDefinitions.EndIpAddressOption); - command.Validators.Add(commandResult => + base.ValidateOptions(options, validationResult); + + var startIpIsValid = !string.IsNullOrEmpty(options.StartIpAddress) && IsValidIpAddress(options.StartIpAddress); + var endIpIsValid = !string.IsNullOrEmpty(options.EndIpAddress) && IsValidIpAddress(options.EndIpAddress); + + if (!startIpIsValid) { - var startIp = commandResult.GetValueOrDefault(SqlOptionDefinitions.StartIpAddressOption); - var endIp = commandResult.GetValueOrDefault(SqlOptionDefinitions.EndIpAddressOption); - - var startIpIsValid = !string.IsNullOrEmpty(startIp) && IsValidIpAddress(startIp); - var endIpIsValid = !string.IsNullOrEmpty(endIp) && IsValidIpAddress(endIp); - - if (!startIpIsValid) - { - commandResult.AddError($"Invalid start IP address format: '{startIp}'. Must be a valid IPv4 address."); - } - - if (!endIpIsValid) - { - commandResult.AddError($"Invalid end IP address format: '{endIp}'. Must be a valid IPv4 address."); - } - - if (startIpIsValid && endIpIsValid && IsDangerousRange(startIp!, endIp!)) - { - commandResult.AddError( - "The specified IP range is not allowed. A range of 0.0.0.0 to 0.0.0.0 enables access from all Azure services, and a range of 0.0.0.0 to 255.255.255.255 opens access to the entire internet. " + - "These overly permissive rules are blocked for security. Specify a narrower IP range instead." - ); - } - }); - } + validationResult.Errors.Add($"Invalid start IP address format: '{options.StartIpAddress}'. Must be a valid IPv4 address."); + } - protected override FirewallRuleCreateOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.FirewallRuleName = parseResult.GetValueOrDefault(SqlOptionDefinitions.FirewallRuleNameOption.Name); - options.StartIpAddress = parseResult.GetValueOrDefault(SqlOptionDefinitions.StartIpAddressOption.Name); - options.EndIpAddress = parseResult.GetValueOrDefault(SqlOptionDefinitions.EndIpAddressOption.Name); - return options; + if (!endIpIsValid) + { + validationResult.Errors.Add($"Invalid end IP address format: '{options.EndIpAddress}'. Must be a valid IPv4 address."); + } + + if (startIpIsValid && endIpIsValid && IsDangerousRange(options.StartIpAddress, options.EndIpAddress)) + { + validationResult.Errors.Add( + "The specified IP range is not allowed. A range of 0.0.0.0 to 0.0.0.0 enables access from all Azure services, and a range of 0.0.0.0 to 255.255.255.255 opens access to the entire internet. " + + "These overly permissive rules are blocked for security. Specify a narrower IP range instead." + ); + } } // IP address must be a dotted-quad IPv4 format (e.g. 10.0.0.1). @@ -100,24 +84,17 @@ internal static bool IsDangerousRange(string startIp, string endIp) return false; } - public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) + public override async Task ExecuteAsync(CommandContext context, FirewallRuleCreateOptions options, CancellationToken cancellationToken) { - if (!Validate(parseResult.CommandResult, context.Response).IsValid) - { - return context.Response; - } - - var options = BindOptions(parseResult); - try { var firewallRule = await _sqlService.CreateFirewallRuleAsync( - options.Server!, - options.ResourceGroup!, + options.Server, + options.ResourceGroup, options.Subscription!, - options.FirewallRuleName!, - options.StartIpAddress!, - options.EndIpAddress!, + options.FirewallRuleName, + options.StartIpAddress, + options.EndIpAddress, options.RetryPolicy, cancellationToken); @@ -147,12 +124,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch - { - RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, - ArgumentException => HttpStatusCode.BadRequest, - _ => base.GetStatusCode(ex) - }; - - internal record FirewallRuleCreateResult(SqlServerFirewallRule FirewallRule); + public sealed record FirewallRuleCreateResult(SqlServerFirewallRule FirewallRule); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleDeleteCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleDeleteCommand.cs index e2b437285d..24c72f1ba8 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleDeleteCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleDeleteCommand.cs @@ -2,12 +2,12 @@ // Licensed under the MIT License. using System.Net; -using Azure.Mcp.Tools.Sql.Options; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Options.FirewallRule; using Azure.Mcp.Tools.Sql.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.Sql.Commands.FirewallRule; @@ -17,10 +17,8 @@ namespace Azure.Mcp.Tools.Sql.Commands.FirewallRule; Name = "delete", Title = "Delete SQL Server Firewall Rule", Description = """ - Deletes a firewall rule from a SQL server. This operation removes the specified - firewall rule, potentially restricting access for the IP addresses that were - previously allowed by this rule. The operation is idempotent - if the rule - doesn't exist, no error is returned. + Deletes a firewall rule from a SQL server, potentially restricting access for the IP addresses that were + previously allowed by this rule. The operation is idempotent - if the rule doesn't exist, no error is returned. """, Destructive = true, Idempotent = true, @@ -28,40 +26,21 @@ Deletes a firewall rule from a SQL server. This operation removes the specified ReadOnly = false, Secret = false, LocalRequired = false)] -public sealed class FirewallRuleDeleteCommand(ISqlService sqlService, ILogger logger) - : BaseSqlCommand(logger) +public sealed class FirewallRuleDeleteCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, FirewallRuleDeleteOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.FirewallRuleNameOption); - } - - protected override FirewallRuleDeleteOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.FirewallRuleName = parseResult.GetValueOrDefault(SqlOptionDefinitions.FirewallRuleNameOption.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); - try { var deleted = await _sqlService.DeleteFirewallRuleAsync( - options.Server!, - options.ResourceGroup!, + options.Server, + options.ResourceGroup, options.Subscription!, - options.FirewallRuleName!, + options.FirewallRuleName, options.RetryPolicy, cancellationToken); @@ -89,12 +68,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch - { - RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, - ArgumentException => HttpStatusCode.BadRequest, - _ => base.GetStatusCode(ex) - }; - - internal record FirewallRuleDeleteResult(bool Deleted, string RuleName); + public sealed record FirewallRuleDeleteResult(bool Deleted, string RuleName); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleListCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleListCommand.cs index 4a01c905e9..b22ba44190 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleListCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleListCommand.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.FirewallRule; using Azure.Mcp.Tools.Sql.Services; @@ -16,8 +18,7 @@ namespace Azure.Mcp.Tools.Sql.Commands.FirewallRule; Name = "list", Title = "List SQL Server Firewall Rules", Description = """ - Gets a list of firewall rules for a SQL server. This command retrieves all - firewall rules configured for the specified SQL server, including their IP address ranges + Gets/retrieves a list of all firewall rules configured for a SQL server, including their IP address ranges and rule names. Returns an array of firewall rule objects with their properties. """, Destructive = false, @@ -26,25 +27,19 @@ and rule names. Returns an array of firewall rule objects with their properties. ReadOnly = true, Secret = false, LocalRequired = false)] -public sealed class FirewallRuleListCommand(ISqlService sqlService, ILogger logger) - : BaseSqlCommand(logger) +public sealed class FirewallRuleListCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) + public override async Task ExecuteAsync(CommandContext context, FirewallRuleListOptions options, CancellationToken cancellationToken) { - if (!Validate(parseResult.CommandResult, context.Response).IsValid) - { - return context.Response; - } - - var options = BindOptions(parseResult); - try { var firewallRules = await _sqlService.ListFirewallRulesAsync( - options.Server!, - options.ResourceGroup!, + options.Server, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -72,5 +67,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - internal record FirewallRuleListResult(List FirewallRules); + public sealed record FirewallRuleListResult(List FirewallRules); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerCreateCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerCreateCommand.cs index 6d53d3cf24..bddde472d9 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerCreateCommand.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options; using Azure.Mcp.Tools.Sql.Options.Server; @@ -18,10 +20,9 @@ namespace Azure.Mcp.Tools.Sql.Commands.Server; Name = "create", Title = "Create SQL Server", Description = """ - Creates a new Azure SQL server in the specified resource group and location. - The server will be created with the specified administrator credentials and - optional configuration settings. Returns the created server with its properties - including the fully qualified domain name. + Creates a new Azure SQL server in the specified resource group and location, with the specified administrator + credentials and optional configuration settings. Returns the created server with its properties including the + fully qualified domain name. """, Destructive = true, Idempotent = false, @@ -29,50 +30,23 @@ including the fully qualified domain name. ReadOnly = false, Secret = false, LocalRequired = false)] -public sealed class ServerCreateCommand(ISqlService sqlService, ILogger logger) - : BaseSqlCommand(logger) +public sealed class ServerCreateCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, ServerCreateOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.AdministratorLoginOption); - command.Options.Add(SqlOptionDefinitions.AdministratorPasswordOption); - command.Options.Add(SqlOptionDefinitions.LocationOption); - command.Options.Add(SqlOptionDefinitions.VersionOption); - command.Options.Add(SqlOptionDefinitions.PublicNetworkAccessOption); - } - - protected override ServerCreateOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.AdministratorLogin = parseResult.GetValueOrDefault(SqlOptionDefinitions.AdministratorLoginOption.Name); - options.AdministratorPassword = parseResult.GetValueOrDefault(SqlOptionDefinitions.AdministratorPasswordOption.Name); - options.Location = parseResult.GetValueOrDefault(SqlOptionDefinitions.LocationOption.Name); - options.Version = parseResult.GetValueOrDefault(SqlOptionDefinitions.VersionOption.Name); - options.PublicNetworkAccess = parseResult.GetValueOrDefault(SqlOptionDefinitions.PublicNetworkAccessOption.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); - try { var server = await _sqlService.CreateServerAsync( - options.Server!, - options.ResourceGroup!, + options.Server, + options.ResourceGroup, options.Subscription!, - options.Location!, - options.AdministratorLogin!, - options.AdministratorPassword!, + options.Location, + options.AdministratorLogin, + options.AdministratorPassword, options.Version, options.PublicNetworkAccess, options.RetryPolicy, @@ -104,12 +78,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch - { - RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, - ArgumentException => HttpStatusCode.BadRequest, - _ => base.GetStatusCode(ex) - }; - - internal record ServerCreateResult(SqlServer Server); + public sealed record ServerCreateResult(SqlServer Server); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerDeleteCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerDeleteCommand.cs index 77248911a2..d6979697ee 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerDeleteCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerDeleteCommand.cs @@ -2,12 +2,12 @@ // Licensed under the MIT License. using System.Net; -using Azure.Mcp.Tools.Sql.Options; +using Azure.Mcp.Core.Commands.Subscription; +using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Tools.Sql.Options.Server; using Azure.Mcp.Tools.Sql.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.Sql.Commands.Server; @@ -27,33 +27,14 @@ This operation permanently deletes all server data and cannot be reversed. ReadOnly = false, Secret = false, LocalRequired = false)] -public sealed class ServerDeleteCommand(ISqlService sqlService, ILogger logger) - : BaseSqlCommand(logger) +public sealed class ServerDeleteCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; + private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, ServerDeleteOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(SqlOptionDefinitions.ForceOption); - } - - protected override ServerDeleteOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.Force = parseResult.GetValueOrDefault(SqlOptionDefinitions.ForceOption.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); - try { // Show warning about destructive operation unless force is specified @@ -68,8 +49,8 @@ public override async Task ExecuteAsync(CommandContext context, } var deleted = await _sqlService.DeleteServerAsync( - options.Server!, - options.ResourceGroup!, + options.Server, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -108,12 +89,5 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch - { - RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, - ArgumentException => HttpStatusCode.BadRequest, - _ => base.GetStatusCode(ex) - }; - - internal record ServerDeleteResult(string Message, bool Success); + public sealed record ServerDeleteResult(string Message, bool Success); } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerGetCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerGetCommand.cs index af2499eb9e..35bb9c77e9 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerGetCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerGetCommand.cs @@ -3,14 +3,13 @@ using System.Net; using Azure.Mcp.Core.Commands.Subscription; -using Azure.Mcp.Tools.Sql.Options; +using Azure.Mcp.Core.Services.Azure.Subscription; +using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.Server; using Azure.Mcp.Tools.Sql.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.Sql.Commands.Server; @@ -31,43 +30,21 @@ including configuration details and current state. ReadOnly = true, Secret = false, LocalRequired = false)] -public sealed class ServerGetCommand(ISqlService sqlService, ILogger logger) - : SubscriptionCommand +public sealed class ServerGetCommand(ISqlService sqlService, ILogger logger, ISubscriptionResolver subscriptionResolver) + : SubscriptionCommand>(subscriptionResolver) { private readonly ISqlService _sqlService = sqlService; private readonly ILogger _logger = logger; - protected override void RegisterOptions(Command command) + public override async Task ExecuteAsync(CommandContext context, ServerGetOptions options, CancellationToken cancellationToken) { - base.RegisterOptions(command); - command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsRequired()); - command.Options.Add(SqlOptionDefinitions.Server.AsOptional()); - } - - protected override ServerGetOptions BindOptions(ParseResult parseResult) - { - var options = base.BindOptions(parseResult); - options.ResourceGroup ??= parseResult.GetValueOrDefault(OptionDefinitions.Common.ResourceGroup.Name); - options.Server = parseResult.GetValueOrDefault(SqlOptionDefinitions.Server.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); - try { if (!string.IsNullOrEmpty(options.Server)) { var server = await _sqlService.GetServerAsync( options.Server, - options.ResourceGroup!, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -77,7 +54,7 @@ public override async Task ExecuteAsync(CommandContext context, else { var servers = await _sqlService.ListServersAsync( - options.ResourceGroup!, + options.ResourceGroup, options.Subscription!, options.RetryPolicy, cancellationToken); @@ -105,5 +82,4 @@ public override async Task ExecuteAsync(CommandContext context, RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) }; - } diff --git a/tools/Azure.Mcp.Tools.Sql/src/GlobalUsings.cs b/tools/Azure.Mcp.Tools.Sql/src/GlobalUsings.cs deleted file mode 100644 index b41cc886b4..0000000000 --- a/tools/Azure.Mcp.Tools.Sql/src/GlobalUsings.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -global using System.CommandLine; diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/BaseDatabaseOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/BaseDatabaseOptions.cs deleted file mode 100644 index 2a08e61fb9..0000000000 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/BaseDatabaseOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Text.Json.Serialization; - -namespace Azure.Mcp.Tools.Sql.Options; - -public class BaseDatabaseOptions : BaseSqlOptions -{ - [JsonPropertyName(SqlOptionDefinitions.DatabaseName)] - public string? Database { get; set; } -} diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/BaseElasticPoolOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/BaseElasticPoolOptions.cs deleted file mode 100644 index e7c8ce4559..0000000000 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/BaseElasticPoolOptions.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Azure.Mcp.Tools.Sql.Options; - -public class BaseElasticPoolOptions : BaseSqlOptions; diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/BaseSqlOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/BaseSqlOptions.cs index c2e081a28f..ea401d977d 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/BaseSqlOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/BaseSqlOptions.cs @@ -1,13 +1,19 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json.Serialization; +using Azure.Mcp.Core.Options; using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.Sql.Options; -public class BaseSqlOptions : SubscriptionOptions +public class BaseSqlOptions : ISubscriptionOption { - [JsonPropertyName(SqlOptionDefinitions.ServerName)] - public string? Server { get; set; } + [Option(Description = OptionDescriptions.ResourceGroup)] + public required string ResourceGroup { get; set; } + + [Option(Description = OptionDescriptions.Subscription)] + public string? Subscription { get; set; } + + [OptionContainer(Prefix = "retry")] + public RetryPolicyOptions? RetryPolicy { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseCreateOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseCreateOptions.cs index f11c749639..8ff40f49af 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseCreateOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseCreateOptions.cs @@ -1,33 +1,39 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json.Serialization; +using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.Sql.Options.Database; -public class DatabaseCreateOptions : BaseDatabaseOptions +public sealed class DatabaseCreateOptions : BaseSqlOptions { - [JsonPropertyName(SqlOptionDefinitions.SkuName)] + [Option(Description = SqlOptionDescriptions.SkuName)] public string? SkuName { get; set; } - [JsonPropertyName(SqlOptionDefinitions.SkuTier)] + [Option(Description = SqlOptionDescriptions.SkuTier)] public string? SkuTier { get; set; } - [JsonPropertyName(SqlOptionDefinitions.SkuCapacity)] + [Option(Description = SqlOptionDescriptions.SkuCapacity)] public int? SkuCapacity { get; set; } - [JsonPropertyName(SqlOptionDefinitions.Collation)] + [Option(Description = SqlOptionDescriptions.Collation)] public string? Collation { get; set; } - [JsonPropertyName(SqlOptionDefinitions.MaxSizeBytes)] + [Option(Description = SqlOptionDescriptions.MaxSizeBytes)] public long? MaxSizeBytes { get; set; } - [JsonPropertyName(SqlOptionDefinitions.ElasticPoolName)] + [Option(Description = SqlOptionDescriptions.ElasticPoolName)] public string? ElasticPoolName { get; set; } - [JsonPropertyName(SqlOptionDefinitions.ZoneRedundant)] + [Option(Description = SqlOptionDescriptions.ZoneRedundant)] public bool? ZoneRedundant { get; set; } - [JsonPropertyName(SqlOptionDefinitions.ReadScale)] + [Option(Description = SqlOptionDescriptions.ReadScale)] public string? ReadScale { get; set; } + + [Option(Description = SqlOptionDescriptions.Database)] + public required string Database { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseDeleteOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseDeleteOptions.cs index 83826e213b..9cf2b82ea9 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseDeleteOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseDeleteOptions.cs @@ -1,8 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Mcp.Core.Options; + namespace Azure.Mcp.Tools.Sql.Options.Database; -public class DatabaseDeleteOptions : BaseDatabaseOptions +public sealed class DatabaseDeleteOptions : BaseSqlOptions { + [Option(Description = SqlOptionDescriptions.Database)] + public required string Database { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseGetOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseGetOptions.cs index 67c81487bb..59e45495a5 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseGetOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseGetOptions.cs @@ -1,8 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Mcp.Core.Options; + namespace Azure.Mcp.Tools.Sql.Options.Database; -public class DatabaseGetOptions : BaseDatabaseOptions +public sealed class DatabaseGetOptions : BaseSqlOptions { + [Option(Description = SqlOptionDescriptions.Database)] + public string? Database { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseRenameOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseRenameOptions.cs index fdf61d74f2..d6bb577107 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseRenameOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseRenameOptions.cs @@ -1,12 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json.Serialization; +using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.Sql.Options.Database; -public class DatabaseRenameOptions : BaseDatabaseOptions +public sealed class DatabaseRenameOptions : BaseSqlOptions { - [JsonPropertyName(SqlOptionDefinitions.NewDatabaseName)] - public string? NewDatabaseName { get; set; } + [Option(Description = "The new name for the Azure SQL Database.")] + public required string NewDatabaseName { get; set; } + + [Option(Description = SqlOptionDescriptions.Database)] + public required string Database { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseUpdateOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseUpdateOptions.cs index df85fc7d3f..041dc2c5f9 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseUpdateOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/Database/DatabaseUpdateOptions.cs @@ -1,33 +1,39 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json.Serialization; +using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.Sql.Options.Database; -public class DatabaseUpdateOptions : BaseDatabaseOptions +public sealed class DatabaseUpdateOptions : BaseSqlOptions { - [JsonPropertyName(SqlOptionDefinitions.SkuName)] + [Option(Description = SqlOptionDescriptions.SkuName)] public string? SkuName { get; set; } - [JsonPropertyName(SqlOptionDefinitions.SkuTier)] + [Option(Description = SqlOptionDescriptions.SkuTier)] public string? SkuTier { get; set; } - [JsonPropertyName(SqlOptionDefinitions.SkuCapacity)] + [Option(Description = SqlOptionDescriptions.SkuCapacity)] public int? SkuCapacity { get; set; } - [JsonPropertyName(SqlOptionDefinitions.Collation)] + [Option(Description = SqlOptionDescriptions.Collation)] public string? Collation { get; set; } - [JsonPropertyName(SqlOptionDefinitions.MaxSizeBytes)] + [Option(Description = SqlOptionDescriptions.MaxSizeBytes)] public long? MaxSizeBytes { get; set; } - [JsonPropertyName(SqlOptionDefinitions.ElasticPoolName)] + [Option(Description = SqlOptionDescriptions.ElasticPoolName)] public string? ElasticPoolName { get; set; } - [JsonPropertyName(SqlOptionDefinitions.ZoneRedundant)] + [Option(Description = SqlOptionDescriptions.ZoneRedundant)] public bool? ZoneRedundant { get; set; } - [JsonPropertyName(SqlOptionDefinitions.ReadScale)] + [Option(Description = SqlOptionDescriptions.ReadScale)] public string? ReadScale { get; set; } + + [Option(Description = SqlOptionDescriptions.Database)] + public required string Database { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/ElasticPool/ElasticPoolListOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/ElasticPool/ElasticPoolListOptions.cs index 0587934edb..2376e573f9 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/ElasticPool/ElasticPoolListOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/ElasticPool/ElasticPoolListOptions.cs @@ -1,6 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Mcp.Core.Options; + namespace Azure.Mcp.Tools.Sql.Options.ElasticPool; -public class ElasticPoolListOptions : BaseElasticPoolOptions; +public sealed class ElasticPoolListOptions : BaseSqlOptions +{ + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } +} diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/EntraAdmin/EntraAdminListOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/EntraAdmin/EntraAdminListOptions.cs index c71b170ad3..d42100608e 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/EntraAdmin/EntraAdminListOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/EntraAdmin/EntraAdminListOptions.cs @@ -1,12 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Mcp.Core.Options; + namespace Azure.Mcp.Tools.Sql.Options.EntraAdmin; /// /// Options for the SQL Server Entra ID Admin List command. /// -public class EntraAdminListOptions : BaseSqlOptions +public sealed class EntraAdminListOptions : BaseSqlOptions { - // No additional options needed - inherits subscription, resource group, and server from BaseSqlOptions + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleCreateOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleCreateOptions.cs index 8f4d17908a..1e67702866 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleCreateOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleCreateOptions.cs @@ -1,18 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json.Serialization; +using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.Sql.Options.FirewallRule; -public class FirewallRuleCreateOptions : BaseSqlOptions +public sealed class FirewallRuleCreateOptions : BaseSqlOptions { - [JsonPropertyName(SqlOptionDefinitions.FirewallRuleName)] - public string? FirewallRuleName { get; set; } + [Option(Description = SqlOptionDescriptions.FirewallRuleName)] + public required string FirewallRuleName { get; set; } - [JsonPropertyName(SqlOptionDefinitions.StartIpAddress)] - public string? StartIpAddress { get; set; } + [Option(Description = "The start IP address of the firewall rule range.")] + public required string StartIpAddress { get; set; } - [JsonPropertyName(SqlOptionDefinitions.EndIpAddress)] - public string? EndIpAddress { get; set; } + [Option(Description = "The end IP address of the firewall rule range.")] + public required string EndIpAddress { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleDeleteOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleDeleteOptions.cs index 2f10a76a0a..a92b040fde 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleDeleteOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleDeleteOptions.cs @@ -1,12 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json.Serialization; +using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.Sql.Options.FirewallRule; -public class FirewallRuleDeleteOptions : BaseSqlOptions +public sealed class FirewallRuleDeleteOptions : BaseSqlOptions { - [JsonPropertyName(SqlOptionDefinitions.FirewallRuleName)] - public string? FirewallRuleName { get; set; } + [Option(Description = SqlOptionDescriptions.FirewallRuleName)] + public required string FirewallRuleName { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleListOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleListOptions.cs index c771c16d29..a203ae389b 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleListOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/FirewallRule/FirewallRuleListOptions.cs @@ -1,12 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Mcp.Core.Options; + namespace Azure.Mcp.Tools.Sql.Options.FirewallRule; /// /// Options for the SQL Server Firewall Rules List command. /// -public class FirewallRuleListOptions : BaseSqlOptions +public sealed class FirewallRuleListOptions : BaseSqlOptions { - // No additional options needed - inherits subscription, resource group, and server from BaseSqlOptions + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerCreateOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerCreateOptions.cs index 575232223e..17eead31c6 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerCreateOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerCreateOptions.cs @@ -1,24 +1,27 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json.Serialization; +using Microsoft.Mcp.Core.Options; namespace Azure.Mcp.Tools.Sql.Options.Server; -public class ServerCreateOptions : BaseSqlOptions +public sealed class ServerCreateOptions : BaseSqlOptions { - [JsonPropertyName(SqlOptionDefinitions.AdministratorLogin)] - public string? AdministratorLogin { get; set; } + [Option(Description = "The administrator login name for the SQL server.")] + public required string AdministratorLogin { get; set; } - [JsonPropertyName(SqlOptionDefinitions.AdministratorPassword)] - public string? AdministratorPassword { get; set; } + [Option(Description = "The administrator password for the SQL server.")] + public required string AdministratorPassword { get; set; } - [JsonPropertyName(SqlOptionDefinitions.Location)] - public string? Location { get; set; } + [Option(Description = "The Azure region location where the SQL server will be created.")] + public required string Location { get; set; } - [JsonPropertyName(SqlOptionDefinitions.Version)] + [Option(Description = "The version of SQL Server to create (e.g., '12.0').")] public string? Version { get; set; } - [JsonPropertyName(SqlOptionDefinitions.PublicNetworkAccess)] + [Option(Description = "Whether public network access is enabled for the SQL server ('Enabled' or 'Disabled'). Defaults to 'Disabled'.")] public string? PublicNetworkAccess { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerDeleteOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerDeleteOptions.cs index 4ff9e94f2b..dba1ac2ef2 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerDeleteOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerDeleteOptions.cs @@ -1,15 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Mcp.Core.Options; + namespace Azure.Mcp.Tools.Sql.Options.Server; /// /// Options for the SQL server delete command. /// -public class ServerDeleteOptions : BaseSqlOptions +public sealed class ServerDeleteOptions : BaseSqlOptions { /// /// Gets or sets whether to force delete the server without confirmation. /// + [Option(Description = "Force delete the server without confirmation prompts.")] public bool Force { get; set; } + + [Option(Description = SqlOptionDescriptions.Server)] + public required string Server { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerGetOptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerGetOptions.cs index c328836c55..e687155317 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerGetOptions.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/Server/ServerGetOptions.cs @@ -1,6 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Mcp.Core.Options; + namespace Azure.Mcp.Tools.Sql.Options.Server; -public class ServerGetOptions : BaseSqlOptions; +public sealed class ServerGetOptions : BaseSqlOptions +{ + [Option(Description = SqlOptionDescriptions.Server)] + public string? Server { get; set; } +} diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/SqlOptionDefinitions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/SqlOptionDefinitions.cs deleted file mode 100644 index ee8d4d080c..0000000000 --- a/tools/Azure.Mcp.Tools.Sql/src/Options/SqlOptionDefinitions.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Azure.Mcp.Tools.Sql.Options; - -public static class SqlOptionDefinitions -{ - public const string ServerName = "server"; - public const string DatabaseName = "database"; - public const string NewDatabaseName = "new-database-name"; - public const string FirewallRuleName = "firewall-rule-name"; - public const string StartIpAddress = "start-ip-address"; - public const string EndIpAddress = "end-ip-address"; - public const string AdministratorLogin = "administrator-login"; - public const string AdministratorPassword = "administrator-password"; - public const string Location = "location"; - public const string Version = "version"; - public const string PublicNetworkAccess = "public-network-access"; - public const string Force = "force"; - public const string SkuName = "sku-name"; - public const string SkuTier = "sku-tier"; - public const string SkuCapacity = "sku-capacity"; - public const string Collation = "collation"; - public const string MaxSizeBytes = "max-size-bytes"; - public const string ElasticPoolName = "elastic-pool-name"; - public const string ZoneRedundant = "zone-redundant"; - public const string ReadScale = "read-scale"; - - public static readonly Option Server = new( - $"--{ServerName}" - ) - { - Description = "The Azure SQL Server name.", - Required = true - }; - - public static readonly Option Database = new( - $"--{DatabaseName}" - ) - { - Description = "The Azure SQL Database name.", - Required = true - }; - - public static readonly Option NewDatabaseNameOption = new( - $"--{NewDatabaseName}" - ) - { - Description = "The new name for the Azure SQL Database.", - Required = true - }; - - public static readonly Option FirewallRuleNameOption = new( - $"--{FirewallRuleName}" - ) - { - Description = "The name of the firewall rule.", - Required = true - }; - - public static readonly Option StartIpAddressOption = new( - $"--{StartIpAddress}" - ) - { - Description = "The start IP address of the firewall rule range.", - Required = true - }; - - public static readonly Option EndIpAddressOption = new( - $"--{EndIpAddress}" - ) - { - Description = "The end IP address of the firewall rule range.", - Required = true - }; - - public static readonly Option AdministratorLoginOption = new( - $"--{AdministratorLogin}" - ) - { - Description = "The administrator login name for the SQL server.", - Required = true - }; - - public static readonly Option AdministratorPasswordOption = new( - $"--{AdministratorPassword}" - ) - { - Description = "The administrator password for the SQL server.", - Required = true - }; - - public static readonly Option LocationOption = new( - $"--{Location}" - ) - { - Description = "The Azure region location where the SQL server will be created.", - Required = true - }; - - public static readonly Option VersionOption = new( - $"--{Version}" - ) - { - Description = "The version of SQL Server to create (e.g., '12.0').", - Required = false - }; - - public static readonly Option PublicNetworkAccessOption = new( - $"--{PublicNetworkAccess}" - ) - { - Description = "Whether public network access is enabled for the SQL server ('Enabled' or 'Disabled'). Defaults to 'Disabled'.", - Required = false - }; - - public static readonly Option ForceOption = new( - $"--{Force}" - ) - { - Description = "Force delete the server without confirmation prompts.", - Required = false - }; - - public static readonly Option SkuNameOption = new( - $"--{SkuName}" - ) - { - Description = "The SKU name for the database (e.g., Basic, S0, P1, GP_Gen5_2).", - Required = false - }; - - public static readonly Option SkuTierOption = new( - $"--{SkuTier}" - ) - { - Description = "The SKU tier for the database (e.g., Basic, Standard, Premium, GeneralPurpose).", - Required = false - }; - - public static readonly Option SkuCapacityOption = new( - $"--{SkuCapacity}" - ) - { - Description = "The SKU capacity (DTU or vCore count) for the database.", - Required = false - }; - - public static readonly Option CollationOption = new( - $"--{Collation}" - ) - { - Description = "The collation for the database (e.g., SQL_Latin1_General_CP1_CI_AS).", - Required = false - }; - - public static readonly Option MaxSizeBytesOption = new( - $"--{MaxSizeBytes}" - ) - { - Description = "The maximum size of the database in bytes.", - Required = false - }; - - public static readonly Option ElasticPoolNameOption = new( - $"--{ElasticPoolName}" - ) - { - Description = "The name of the elastic pool to assign the database to.", - Required = false - }; - - public static readonly Option ZoneRedundantOption = new( - $"--{ZoneRedundant}" - ) - { - Description = "Whether the database should be zone redundant.", - Required = false - }; - - public static readonly Option ReadScaleOption = new( - $"--{ReadScale}" - ) - { - Description = "Read scale option for the database (Enabled or Disabled).", - Required = false - }; -} diff --git a/tools/Azure.Mcp.Tools.Sql/src/Options/SqlOptionDescriptions.cs b/tools/Azure.Mcp.Tools.Sql/src/Options/SqlOptionDescriptions.cs new file mode 100644 index 0000000000..5ff5e069ab --- /dev/null +++ b/tools/Azure.Mcp.Tools.Sql/src/Options/SqlOptionDescriptions.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Azure.Mcp.Tools.Sql.Options; + +internal static class SqlOptionDescriptions +{ + internal const string Server = "The Azure SQL Server name."; + internal const string Database = "The Azure SQL Database name."; + internal const string FirewallRuleName = "The name of the firewall rule."; + internal const string SkuName = "The SKU name for the database (e.g., Basic, S0, P1, GP_Gen5_2)."; + internal const string SkuTier = "The SKU tier for the database (e.g., Basic, Standard, Premium, GeneralPurpose)."; + internal const string SkuCapacity = "The SKU capacity (DTU or vCore count) for the database."; + internal const string Collation = "The collation for the database (e.g., SQL_Latin1_General_CP1_CI_AS)."; + internal const string MaxSizeBytes = "The maximum size of the database in bytes."; + internal const string ElasticPoolName = "The name of the elastic pool to assign the database to."; + internal const string ZoneRedundant = "Whether the database should be zone redundant."; + internal const string ReadScale = "Read scale option for the database (Enabled or Disabled)."; +} diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlDatabaseData.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlDatabaseData.cs index dc72550188..31005c67c2 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlDatabaseData.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlDatabaseData.cs @@ -5,34 +5,33 @@ using System.Text.Json.Serialization; using Azure.Mcp.Tools.Sql.Commands; -namespace Azure.Mcp.Tools.Sql.Services.Models +namespace Azure.Mcp.Tools.Sql.Services.Models; + +/// +/// A class representing the SqlDatabase data model. +/// A database resource. +/// +internal sealed class SqlDatabaseData { - /// - /// A class representing the SqlDatabase data model. - /// A database resource. - /// - internal sealed class SqlDatabaseData - { - /// The resource ID for the resource. - [JsonPropertyName("id")] - public string? ResourceId { get; set; } - /// The type of the resource. - [JsonPropertyName("type")] - public string? ResourceType { get; set; } - /// The name of the resource. - [JsonPropertyName("name")] - public string? ResourceName { get; set; } - /// The location of the resource. - public string? Location { get; set; } - /// The database SKU. - public SqlSku? Sku { get; set; } - /// Properties of the Sql database. - public SqlDatabaseProperties? Properties { get; set; } + /// The resource ID for the resource. + [JsonPropertyName("id")] + public string? ResourceId { get; set; } + /// The type of the resource. + [JsonPropertyName("type")] + public string? ResourceType { get; set; } + /// The name of the resource. + [JsonPropertyName("name")] + public string? ResourceName { get; set; } + /// The location of the resource. + public string? Location { get; set; } + /// The database SKU. + public SqlSku? Sku { get; set; } + /// Properties of the Sql database. + public SqlDatabaseProperties? Properties { get; set; } - // Read the JSON response content and create a model instance from it. - public static SqlDatabaseData? FromJson(JsonElement source) - { - return JsonSerializer.Deserialize(source, SqlJsonContext.Default.SqlDatabaseData); - } + // Read the JSON response content and create a model instance from it. + public static SqlDatabaseData? FromJson(JsonElement source) + { + return JsonSerializer.Deserialize(source, SqlJsonContext.Default.SqlDatabaseData); } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlDatabaseProperties.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlDatabaseProperties.cs index e8b4f3e143..e745add33b 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlDatabaseProperties.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlDatabaseProperties.cs @@ -2,37 +2,36 @@ // Licensed under the MIT License. using System.Text.Json.Serialization; -namespace Azure.Mcp.Tools.Sql.Services.Models +namespace Azure.Mcp.Tools.Sql.Services.Models; + +/// +/// A class representing the SqlDatabase properties model. +/// A database resource properties. +/// +internal sealed class SqlDatabaseProperties { - /// - /// A class representing the SqlDatabase properties model. - /// A database resource properties. - /// - internal sealed class SqlDatabaseProperties - { - /// The collation of the database. - public string? Collation { get; set; } - /// The max size of the database expressed in bytes. - public long? MaxSizeBytes { get; set; } - /// The resource identifier of the elastic pool containing this database. - public string? ElasticPoolId { get; set; } - /// The status of the database. - public string? Status { get; set; } - /// The creation date of the database (ISO8601 format). - [JsonPropertyName("creationDate")] - public DateTimeOffset? CreatedOn { get; set; } - /// The current service level objective name of the database. - public string? CurrentServiceObjectiveName { get; set; } - /// The license type to apply for this database. `LicenseIncluded` if you need a license, or `BasePrice` if you have a license and are eligible for the Azure Hybrid Benefit. - public string? LicenseType { get; set; } - /// This records the earliest start date and time that restore is available for this database (ISO8601 format). - [JsonPropertyName("earliestRestoreDate")] - public DateTimeOffset? EarliestRestoreOn { get; set; } - /// The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool. - public string? ReadScale { get; set; } - /// Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones. - public bool? IsZoneRedundant { get; set; } - /// The name and tier of the SKU. - public SqlSku? CurrentSku { get; set; } - } + /// The collation of the database. + public string? Collation { get; set; } + /// The max size of the database expressed in bytes. + public long? MaxSizeBytes { get; set; } + /// The resource identifier of the elastic pool containing this database. + public string? ElasticPoolId { get; set; } + /// The status of the database. + public string? Status { get; set; } + /// The creation date of the database (ISO8601 format). + [JsonPropertyName("creationDate")] + public DateTimeOffset? CreatedOn { get; set; } + /// The current service level objective name of the database. + public string? CurrentServiceObjectiveName { get; set; } + /// The license type to apply for this database. `LicenseIncluded` if you need a license, or `BasePrice` if you have a license and are eligible for the Azure Hybrid Benefit. + public string? LicenseType { get; set; } + /// This records the earliest start date and time that restore is available for this database (ISO8601 format). + [JsonPropertyName("earliestRestoreDate")] + public DateTimeOffset? EarliestRestoreOn { get; set; } + /// The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool. + public string? ReadScale { get; set; } + /// Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones. + public bool? IsZoneRedundant { get; set; } + /// The name and tier of the SKU. + public SqlSku? CurrentSku { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlFirewallRuleData.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlFirewallRuleData.cs index cce7f1f86d..307ca5f930 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlFirewallRuleData.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlFirewallRuleData.cs @@ -5,30 +5,29 @@ using System.Text.Json.Serialization; using Azure.Mcp.Tools.Sql.Commands; -namespace Azure.Mcp.Tools.Sql.Services.Models +namespace Azure.Mcp.Tools.Sql.Services.Models; + +/// +/// A class representing the SqlFirewallRule data model. +/// A server firewall rule. +/// +internal sealed class SqlFirewallRuleData { - /// - /// A class representing the SqlFirewallRule data model. - /// A server firewall rule. - /// - internal sealed class SqlFirewallRuleData - { - /// The resource ID for the resource. - [JsonPropertyName("id")] - public string? ResourceId { get; set; } - /// The type of the resource. - [JsonPropertyName("type")] - public string? ResourceType { get; set; } - /// The name of the resource. - [JsonPropertyName("name")] - public string? ResourceName { get; set; } - /// The properties of the firewall rule. - public SqlFirewallRuleProperties? Properties { get; set; } + /// The resource ID for the resource. + [JsonPropertyName("id")] + public string? ResourceId { get; set; } + /// The type of the resource. + [JsonPropertyName("type")] + public string? ResourceType { get; set; } + /// The name of the resource. + [JsonPropertyName("name")] + public string? ResourceName { get; set; } + /// The properties of the firewall rule. + public SqlFirewallRuleProperties? Properties { get; set; } - // Read the JSON response content and create a model instance from it. - public static SqlFirewallRuleData? FromJson(JsonElement source) - { - return JsonSerializer.Deserialize(source, SqlJsonContext.Default.SqlFirewallRuleData); - } + // Read the JSON response content and create a model instance from it. + public static SqlFirewallRuleData? FromJson(JsonElement source) + { + return JsonSerializer.Deserialize(source, SqlJsonContext.Default.SqlFirewallRuleData); } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlFirewallRuleProperties.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlFirewallRuleProperties.cs index 6b97770162..29830d25c7 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlFirewallRuleProperties.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlFirewallRuleProperties.cs @@ -3,19 +3,18 @@ using System.Text.Json.Serialization; -namespace Azure.Mcp.Tools.Sql.Services.Models +namespace Azure.Mcp.Tools.Sql.Services.Models; + +/// +/// A class representing the SqlFirewallRule properties data model. +/// A server firewall rule properties. +/// +internal sealed class SqlFirewallRuleProperties { - /// - /// A class representing the SqlFirewallRule properties data model. - /// A server firewall rule properties. - /// - internal sealed class SqlFirewallRuleProperties - { - /// The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses. - [JsonPropertyName("startIpAddress")] - public string? StartIPAddress { get; set; } - /// The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses. - [JsonPropertyName("endIpAddress")] - public string? EndIPAddress { get; set; } - } + /// The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses. + [JsonPropertyName("startIpAddress")] + public string? StartIPAddress { get; set; } + /// The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses. + [JsonPropertyName("endIpAddress")] + public string? EndIPAddress { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlServerAadAdministratorData.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlServerAadAdministratorData.cs index fe9c0aebe6..c388378903 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlServerAadAdministratorData.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlServerAadAdministratorData.cs @@ -5,30 +5,29 @@ using System.Text.Json.Serialization; using Azure.Mcp.Tools.Sql.Commands; -namespace Azure.Mcp.Tools.Sql.Services.Models +namespace Azure.Mcp.Tools.Sql.Services.Models; + +/// +/// A class representing the SqlServerAadAdministrator data model. +/// Azure Active Directory administrator. +/// +internal sealed class SqlServerAadAdministratorData { - /// - /// A class representing the SqlServerAadAdministrator data model. - /// Azure Active Directory administrator. - /// - internal sealed class SqlServerAadAdministratorData - { - /// The resource ID for the resource. - [JsonPropertyName("id")] - public string? ResourceId { get; set; } - /// The type of the resource. - [JsonPropertyName("type")] - public string? ResourceType { get; set; } - /// The name of the resource. - [JsonPropertyName("name")] - public string? ResourceName { get; set; } - /// Properties of the Azure Active Directory administrator. - public SqlServerAadAdministratorProperties? Properties { get; set; } + /// The resource ID for the resource. + [JsonPropertyName("id")] + public string? ResourceId { get; set; } + /// The type of the resource. + [JsonPropertyName("type")] + public string? ResourceType { get; set; } + /// The name of the resource. + [JsonPropertyName("name")] + public string? ResourceName { get; set; } + /// Properties of the Azure Active Directory administrator. + public SqlServerAadAdministratorProperties? Properties { get; set; } - // Read the JSON response content and create a model instance from it. - public static SqlServerAadAdministratorData? FromJson(JsonElement source) - { - return JsonSerializer.Deserialize(source, SqlJsonContext.Default.SqlServerAadAdministratorData); - } + // Read the JSON response content and create a model instance from it. + public static SqlServerAadAdministratorData? FromJson(JsonElement source) + { + return JsonSerializer.Deserialize(source, SqlJsonContext.Default.SqlServerAadAdministratorData); } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlServerAadAdministratorProperties.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlServerAadAdministratorProperties.cs index 331937d9d4..7b8845b5ef 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlServerAadAdministratorProperties.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlServerAadAdministratorProperties.cs @@ -3,24 +3,23 @@ using System.Text.Json.Serialization; -namespace Azure.Mcp.Tools.Sql.Services.Models +namespace Azure.Mcp.Tools.Sql.Services.Models; + +/// +/// A class representing the SqlServerAadAdministrator properties model. +/// Azure Active Directory administrator properties. +/// +internal sealed class SqlServerAadAdministratorProperties { - /// - /// A class representing the SqlServerAadAdministrator properties model. - /// Azure Active Directory administrator properties. - /// - internal sealed class SqlServerAadAdministratorProperties - { - /// Type of the sever administrator. - public string? AdministratorType { get; set; } - /// Login name of the server administrator. - public string? Login { get; set; } - /// SID (object ID) of the server administrator. - public Guid? Sid { get; set; } - /// Tenant ID of the administrator. - public Guid? TenantId { get; set; } - /// Azure Active Directory only Authentication enabled. - [JsonPropertyName("azureADOnlyAuthentication")] - public bool? IsAzureADOnlyAuthenticationEnabled { get; set; } - } + /// Type of the sever administrator. + public string? AdministratorType { get; set; } + /// Login name of the server administrator. + public string? Login { get; set; } + /// SID (object ID) of the server administrator. + public Guid? Sid { get; set; } + /// Tenant ID of the administrator. + public Guid? TenantId { get; set; } + /// Azure Active Directory only Authentication enabled. + [JsonPropertyName("azureADOnlyAuthentication")] + public bool? IsAzureADOnlyAuthenticationEnabled { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlSku.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlSku.cs index db39a7ba55..48736f3460 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlSku.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/Models/SqlSku.cs @@ -1,20 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -namespace Azure.Mcp.Tools.Sql.Services.Models +namespace Azure.Mcp.Tools.Sql.Services.Models; + +/// An ARM Resource SKU. +internal sealed class SqlSku { - /// An ARM Resource SKU. - internal sealed class SqlSku - { - /// The name of the SKU, typically, a letter + Number code, e.g. P3. - public string? Name { get; set; } - /// The tier or edition of the particular SKU, e.g. Basic, Premium. - public string? Tier { get; set; } - /// Size of the particular SKU. - public string? Size { get; set; } - /// If the service has different generations of hardware, for the same SKU, then that can be captured here. - public string? Family { get; set; } - /// Capacity of the particular SKU. - public int? Capacity { get; set; } - } + /// The name of the SKU, typically, a letter + Number code, e.g. P3. + public string? Name { get; set; } + /// The tier or edition of the particular SKU, e.g. Basic, Premium. + public string? Tier { get; set; } + /// Size of the particular SKU. + public string? Size { get; set; } + /// If the service has different generations of hardware, for the same SKU, then that can be captured here. + public string? Family { get; set; } + /// Capacity of the particular SKU. + public int? Capacity { get; set; } } diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs index e4130c3f5d..e1b92a6a49 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs @@ -8,7 +8,6 @@ using Azure.Mcp.Core.Services.Azure.Subscription; using Azure.Mcp.Core.Services.Azure.Tenant; using Azure.Mcp.Tools.Sql.Models; -using Azure.Mcp.Tools.Sql.Services.Models; using Azure.ResourceManager.Sql; using Azure.ResourceManager.Sql.Models; using Microsoft.Extensions.Logging; @@ -16,7 +15,8 @@ namespace Azure.Mcp.Tools.Sql.Services; -public class SqlService(ISubscriptionService subscriptionService, ITenantService tenantService, ILogger logger) : BaseAzureResourceService(subscriptionService, tenantService), ISqlService +public class SqlService(ISubscriptionService subscriptionService, ITenantService tenantService, ILogger logger) + : BaseAzureResourceService(subscriptionService, tenantService), ISqlService { private readonly ISubscriptionService _subscriptionService = subscriptionService; private readonly ILogger _logger = logger; @@ -145,7 +145,7 @@ public async Task CreateDatabaseAsync( var sqlServerResource = await GetSqlServerResourceAsync(serverName, resourceGroup, subscription, retryPolicy, cancellationToken); - var databaseData = new ResourceManager.Sql.SqlDatabaseData(sqlServerResource.Data.Location); + var databaseData = new SqlDatabaseData(sqlServerResource.Data.Location); // Configure SKU if provided if (!string.IsNullOrEmpty(skuName) || !string.IsNullOrEmpty(skuTier) || skuCapacity.HasValue) @@ -262,7 +262,7 @@ public async Task UpdateDatabaseAsync( // Only preserve values that are explicitly provided or if SKU name isn't changing bool isSkuNameChanging = !string.IsNullOrEmpty(skuName) && skuName != databaseData.Sku?.Name; - var sku = new ResourceManager.Sql.Models.SqlSku(skuName ?? databaseData.Sku?.Name ?? "Basic") + var sku = new SqlSku(skuName ?? databaseData.Sku?.Name ?? "Basic") { Tier = skuTier ?? (isSkuNameChanging ? null : databaseData.Sku?.Tier), Capacity = skuCapacity ?? (isSkuNameChanging ? null : databaseData.Sku?.Capacity), @@ -576,7 +576,7 @@ public async Task CreateFirewallRuleAsync( var sqlServerResource = await GetSqlServerResourceAsync(serverName, resourceGroup, subscription, retryPolicy, cancellationToken); - var firewallRuleData = new ResourceManager.Sql.SqlFirewallRuleData() + var firewallRuleData = new SqlFirewallRuleData() { StartIPAddress = startIpAddress, EndIPAddress = endIpAddress diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseCreateCommandTests.cs index 6f2a2707da..a0c70aa52d 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseCreateCommandTests.cs @@ -2,18 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.Database; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.Database; -public class DatabaseCreateCommandTests : CommandUnitTestsBase +public class DatabaseCreateCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseDeleteCommandTests.cs index 3a45c423ce..41b7a89ae5 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseDeleteCommandTests.cs @@ -2,17 +2,17 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.Database; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.Database; -public class DatabaseDeleteCommandTests : CommandUnitTestsBase +public class DatabaseDeleteCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseGetCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseGetCommandTests.cs index 5450b1cc2b..1234eefa47 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseGetCommandTests.cs @@ -2,18 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.Database; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.Database; -public class DatabaseGetCommandTests : CommandUnitTestsBase +public class DatabaseGetCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseRenameCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseRenameCommandTests.cs index af16d54e9d..6312f81207 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseRenameCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseRenameCommandTests.cs @@ -2,19 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.Database; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; -using Microsoft.Mcp.Tests.Helpers; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.Database; -public class DatabaseRenameCommandTests : CommandUnitTestsBase +public class DatabaseRenameCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() @@ -258,9 +257,6 @@ public async Task ExecuteAsync_HandlesGeneralException() [Fact] public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() { - // Arrange - Test when subscription comes from environment variable - TestEnvironment.SetAzureSubscriptionId("env-sub-id"); - var mockDatabase = new SqlDatabase( Name: "newdb", Id: "/subscriptions/env-sub-id/resourceGroups/rg/providers/Microsoft.Sql/servers/server1/databases/newdb", @@ -291,6 +287,7 @@ public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() // Act var response = await ExecuteCommandAsync( + "--subscription", "env-sub-id", "--resource-group", "rg", "--server", "server1", "--database", "olddb", diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseUpdateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseUpdateCommandTests.cs index a882f3e021..3243fdba29 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseUpdateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseUpdateCommandTests.cs @@ -2,19 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.Database; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; -using Microsoft.Mcp.Tests.Helpers; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.Database; -public class DatabaseUpdateCommandTests : CommandUnitTestsBase +public class DatabaseUpdateCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() @@ -380,9 +379,6 @@ public async Task ExecuteAsync_WithOptionalParameters_Succeeds(string commandArg [Fact] public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() { - // Arrange - Test minimum scope when subscription comes from environment variable - TestEnvironment.SetAzureSubscriptionId("env-sub-id"); - var mockDatabase = new SqlDatabase( Name: "testdb", Id: "/subscriptions/env-sub-id/resourceGroups/rg/providers/Microsoft.Sql/servers/server1/databases/testdb", @@ -420,6 +416,7 @@ public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() // Act var response = await ExecuteCommandAsync( + "--subscription", "env-sub-id", "--resource-group", "rg", "--server", "server1", "--database", "testdb"); diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/ElasticPool/ElasticPoolListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/ElasticPool/ElasticPoolListCommandTests.cs index c37523ca62..1de705a8ca 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/ElasticPool/ElasticPoolListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/ElasticPool/ElasticPoolListCommandTests.cs @@ -2,18 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.ElasticPool; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.ElasticPool; -public class ElasticPoolListCommandTests : CommandUnitTestsBase +public class ElasticPoolListCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/EntraAdmin/EntraAdminListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/EntraAdmin/EntraAdminListCommandTests.cs index f57d1ed03a..654f162808 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/EntraAdmin/EntraAdminListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/EntraAdmin/EntraAdminListCommandTests.cs @@ -2,18 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.EntraAdmin; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.EntraAdmin; -public class EntraAdminListCommandTests : CommandUnitTestsBase +public class EntraAdminListCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleCreateCommandTests.cs index 4964602a1c..3d95b3a22a 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleCreateCommandTests.cs @@ -2,18 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.FirewallRule; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.FirewallRule; -public class FirewallRuleCreateCommandTests : CommandUnitTestsBase +public class FirewallRuleCreateCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleDeleteCommandTests.cs index 7aebacdaa0..83f5114b58 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleDeleteCommandTests.cs @@ -2,17 +2,17 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.FirewallRule; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.FirewallRule; -public class FirewallRuleDeleteCommandTests : CommandUnitTestsBase +public class FirewallRuleDeleteCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleListCommandTests.cs index c0d87415f6..199a6a66cc 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/FirewallRule/FirewallRuleListCommandTests.cs @@ -2,18 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.FirewallRule; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.FirewallRule; -public class FirewallRuleListCommandTests : CommandUnitTestsBase +public class FirewallRuleListCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerCreateCommandTests.cs index cf95faf76c..6dcc5d7104 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerCreateCommandTests.cs @@ -2,18 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.Server; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.Server; -public class ServerCreateCommandTests : CommandUnitTestsBase +public class ServerCreateCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerDeleteCommandTests.cs index 088ecccf1d..08ce675fa6 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerDeleteCommandTests.cs @@ -2,17 +2,17 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.Server; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.Server; -public class ServerDeleteCommandTests : CommandUnitTestsBase +public class ServerDeleteCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerGetCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerGetCommandTests.cs index cc363e402f..3960f8c05b 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Server/ServerGetCommandTests.cs @@ -2,18 +2,18 @@ // Licensed under the MIT License. using System.Net; +using Azure.Mcp.Tests.Commands; using Azure.Mcp.Tools.Sql.Commands.Server; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Services; using Microsoft.Mcp.Core.Options; -using Microsoft.Mcp.Tests.Client; using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; namespace Azure.Mcp.Tools.Sql.Tests.Server; -public class ServerGetCommandTests : CommandUnitTestsBase +public class ServerGetCommandTests : SubscriptionCommandUnitTestsBase { [Fact] public void Constructor_InitializesCommandCorrectly() From 4373a9d3efbed19280e05668e789c2d53d6d7249 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:23:21 -0400 Subject: [PATCH 2/2] Update tests --- .../Database/DatabaseRenameCommandTests.cs | 2 +- .../Database/DatabaseUpdateCommandTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseRenameCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseRenameCommandTests.cs index 6312f81207..b4f84afa34 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseRenameCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseRenameCommandTests.cs @@ -275,6 +275,7 @@ public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() ZoneRedundant: false ); + SubscriptionResolver.ResolveSubscription(null).Returns("env-sub-id"); Service.RenameDatabaseAsync( Arg.Is("server1"), Arg.Is("olddb"), @@ -287,7 +288,6 @@ public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() // Act var response = await ExecuteCommandAsync( - "--subscription", "env-sub-id", "--resource-group", "rg", "--server", "server1", "--database", "olddb", diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseUpdateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseUpdateCommandTests.cs index 3243fdba29..f09dab8693 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseUpdateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.Tests/Database/DatabaseUpdateCommandTests.cs @@ -397,6 +397,7 @@ public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() ZoneRedundant: false ); + SubscriptionResolver.ResolveSubscription(null).Returns("env-sub-id"); Service.UpdateDatabaseAsync( Arg.Is("server1"), Arg.Is("testdb"), @@ -416,7 +417,6 @@ public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() // Act var response = await ExecuteCommandAsync( - "--subscription", "env-sub-id", "--resource-group", "rg", "--server", "server1", "--database", "testdb");