From 6abdeb021b3da327259713cc2967b579a23618c5 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Thu, 18 Sep 2025 15:31:52 -0700 Subject: [PATCH 01/10] Add validation for service start --transport option --- .../Server/Commands/ServiceStartCommand.cs | 18 +++++- .../Areas/Server/ServiceStartCommandTests.cs | 62 +++++++++++++++++++ .../CommandResultExtensionsTests.cs | 1 - .../RecommendationListCommandTests.cs | 2 - .../src/Services/EventGridService.cs | 3 - .../src/Services/IEventGridService.cs | 2 - .../CognitiveServicesAccountDeploymentData.cs | 2 - ...tiveServicesAccountDeploymentProperties.cs | 2 - .../Commands/BaseWorkspaceMonitorCommand.cs | 1 - .../BaseMonitorHealthModelsCommand.cs | 2 - .../Index/IndexGetCommandTests.cs | 1 - 11 files changed, 79 insertions(+), 17 deletions(-) diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs index 0534afdf07..7d55ada957 100644 --- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs +++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs @@ -74,6 +74,7 @@ public override async Task ExecuteAsync(CommandContext context, string[]? namespaces = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Namespace.Name); string? mode = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Mode.Name); bool? readOnly = parseResult.GetValueOrDefault(ServiceOptionDefinitions.ReadOnly.Name); + string? transport = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Transport.Name); var debug = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Debug.Name); @@ -82,6 +83,11 @@ public override async Task ExecuteAsync(CommandContext context, throw new ArgumentException($"Invalid mode '{mode}'. Valid modes are: {ModeTypes.SingleToolProxy}, {ModeTypes.NamespaceProxy}, {ModeTypes.All}."); } + if (!IsValidTransport(transport)) + { + throw new ArgumentException($"Invalid transport '{transport}'. Valid transports are: {TransportTypes.StdIo}."); + } + var enableInsecureTransports = parseResult.GetValueOrDefault(ServiceOptionDefinitions.EnableInsecureTransports.Name); if (enableInsecureTransports) @@ -95,7 +101,7 @@ public override async Task ExecuteAsync(CommandContext context, var serverOptions = new ServiceStartOptions { - Transport = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Transport.Name) ?? TransportTypes.StdIo, + Transport = transport ?? TransportTypes.StdIo, Namespace = namespaces, Mode = mode, ReadOnly = readOnly, @@ -123,6 +129,16 @@ private static bool IsValidMode(string? mode) mode == ModeTypes.All; } + /// + /// Validates if the provided transport is a valid transport type. + /// + /// The transport to validate. + /// True if the transport is valid, otherwise false. + private static bool IsValidTransport(string? transport) + { + return transport == TransportTypes.StdIo; + } + /// /// Creates the host for the MCP server with the specified options. /// diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs index 726cfc93d3..74f3b56a67 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs @@ -4,6 +4,8 @@ using System.CommandLine; using Azure.Mcp.Core.Areas.Server.Commands; using Azure.Mcp.Core.Areas.Server.Options; +using Azure.Mcp.Core.Models.Command; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Azure.Mcp.Core.UnitTests.Areas.Server; @@ -85,6 +87,42 @@ public void AllOptionsRegistered_IncludesInsecureDisableElicitation() Assert.True(hasInsecureDisableElicitationOption, "InsecureDisableElicitation option should be registered"); } + [Theory] + [InlineData("sse")] + [InlineData("websocket")] + [InlineData("http")] + [InlineData("invalid")] + public async Task ExecuteAsync_InvalidTransport_ThrowsArgumentException(string invalidTransport) + { + // Arrange + var parseResult = CreateParseResultWithTransport(invalidTransport); + var serviceProvider = new ServiceCollection().BuildServiceProvider(); + var context = new CommandContext(serviceProvider); + + // Act & Assert + var exception = await Assert.ThrowsAsync( + () => _command.ExecuteAsync(context, parseResult)); + + Assert.Contains($"Invalid transport '{invalidTransport}'", exception.Message); + Assert.Contains("Valid transports are: stdio", exception.Message); + } + + [Fact] + public async Task ExecuteAsync_ValidTransport_DoesNotThrow() + { + // Arrange + var parseResult = CreateParseResultWithTransport("stdio"); + var serviceProvider = new ServiceCollection().BuildServiceProvider(); + var context = new CommandContext(serviceProvider); + + // Act & Assert - This will throw because the server can't actually start in a unit test, + // but it should not throw an ArgumentException about invalid transport + var exception = await Assert.ThrowsAnyAsync( + () => _command.ExecuteAsync(context, parseResult)); + + Assert.IsNotType(exception); + } + private static ParseResult CreateParseResult(string? serviceValue) { var root = new RootCommand @@ -126,4 +164,28 @@ private static ParseResult CreateParseResultWithInsecureDisableElicitation(bool return root.Parse([.. args]); } + + private static ParseResult CreateParseResultWithTransport(string transport) + { + var root = new RootCommand + { + ServiceOptionDefinitions.Namespace, + ServiceOptionDefinitions.Transport, + ServiceOptionDefinitions.Mode, + ServiceOptionDefinitions.ReadOnly, + ServiceOptionDefinitions.Debug, + ServiceOptionDefinitions.EnableInsecureTransports, + ServiceOptionDefinitions.InsecureDisableElicitation + }; + var args = new List + { + "--transport", + transport, + "--mode", + "all", + "--read-only" + }; + + return root.Parse([.. args]); + } } diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Extensions/CommandResultExtensionsTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Extensions/CommandResultExtensionsTests.cs index c4ae8553f5..54c15cf318 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Extensions/CommandResultExtensionsTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Extensions/CommandResultExtensionsTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.CommandLine; -using System.CommandLine.Parsing; using Azure.Mcp.Core.Extensions; using Xunit; diff --git a/tools/Azure.Mcp.Tools.ApplicationInsights/tests/Azure.Mcp.Tools.ApplicationInsights.UnitTests/RecommendationListCommandTests.cs b/tools/Azure.Mcp.Tools.ApplicationInsights/tests/Azure.Mcp.Tools.ApplicationInsights.UnitTests/RecommendationListCommandTests.cs index e6f41fd4e1..8314abcf81 100644 --- a/tools/Azure.Mcp.Tools.ApplicationInsights/tests/Azure.Mcp.Tools.ApplicationInsights.UnitTests/RecommendationListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ApplicationInsights/tests/Azure.Mcp.Tools.ApplicationInsights.UnitTests/RecommendationListCommandTests.cs @@ -1,7 +1,5 @@ -using System.CommandLine.Parsing; using System.Text.Json; using System.Text.Json.Nodes; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.ApplicationInsights.Commands.Recommendation; diff --git a/tools/Azure.Mcp.Tools.EventGrid/src/Services/EventGridService.cs b/tools/Azure.Mcp.Tools.EventGrid/src/Services/EventGridService.cs index 9d83eebd89..a333a952a6 100644 --- a/tools/Azure.Mcp.Tools.EventGrid/src/Services/EventGridService.cs +++ b/tools/Azure.Mcp.Tools.EventGrid/src/Services/EventGridService.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Diagnostics.CodeAnalysis; -using Azure.Mcp.Core.Options; -using Azure.ResourceManager; using Azure.ResourceManager.EventGrid; using Azure.ResourceManager.EventGrid.Models; using Azure.ResourceManager.Resources; diff --git a/tools/Azure.Mcp.Tools.EventGrid/src/Services/IEventGridService.cs b/tools/Azure.Mcp.Tools.EventGrid/src/Services/IEventGridService.cs index f956d66a05..72bbdb6e7c 100644 --- a/tools/Azure.Mcp.Tools.EventGrid/src/Services/IEventGridService.cs +++ b/tools/Azure.Mcp.Tools.EventGrid/src/Services/IEventGridService.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Tools.EventGrid.Models; - namespace Azure.Mcp.Tools.EventGrid.Services; public interface IEventGridService diff --git a/tools/Azure.Mcp.Tools.Foundry/src/Services/Models/CognitiveServicesAccountDeploymentData.cs b/tools/Azure.Mcp.Tools.Foundry/src/Services/Models/CognitiveServicesAccountDeploymentData.cs index 92a6fcc0f9..5441d22d4f 100644 --- a/tools/Azure.Mcp.Tools.Foundry/src/Services/Models/CognitiveServicesAccountDeploymentData.cs +++ b/tools/Azure.Mcp.Tools.Foundry/src/Services/Models/CognitiveServicesAccountDeploymentData.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Text.Json.Serialization; - namespace Azure.Mcp.Tools.Foundry.Services.Models; /// diff --git a/tools/Azure.Mcp.Tools.Foundry/src/Services/Models/CognitiveServicesAccountDeploymentProperties.cs b/tools/Azure.Mcp.Tools.Foundry/src/Services/Models/CognitiveServicesAccountDeploymentProperties.cs index f3ecf31d56..02b85110a2 100644 --- a/tools/Azure.Mcp.Tools.Foundry/src/Services/Models/CognitiveServicesAccountDeploymentProperties.cs +++ b/tools/Azure.Mcp.Tools.Foundry/src/Services/Models/CognitiveServicesAccountDeploymentProperties.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Text.Json.Serialization; - namespace Azure.Mcp.Tools.Foundry.Services.Models; /// Properties of Cognitive Services account deployment. diff --git a/tools/Azure.Mcp.Tools.Monitor/src/Commands/BaseWorkspaceMonitorCommand.cs b/tools/Azure.Mcp.Tools.Monitor/src/Commands/BaseWorkspaceMonitorCommand.cs index 31943003b4..81dffdae8e 100644 --- a/tools/Azure.Mcp.Tools.Monitor/src/Commands/BaseWorkspaceMonitorCommand.cs +++ b/tools/Azure.Mcp.Tools.Monitor/src/Commands/BaseWorkspaceMonitorCommand.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using Azure.Mcp.Core.Commands; -using Azure.Mcp.Core.Models.Option; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Monitor.Options; diff --git a/tools/Azure.Mcp.Tools.Monitor/src/Commands/HealthModels/BaseMonitorHealthModelsCommand.cs b/tools/Azure.Mcp.Tools.Monitor/src/Commands/HealthModels/BaseMonitorHealthModelsCommand.cs index b9839cb7b2..6e53ee1aa8 100644 --- a/tools/Azure.Mcp.Tools.Monitor/src/Commands/HealthModels/BaseMonitorHealthModelsCommand.cs +++ b/tools/Azure.Mcp.Tools.Monitor/src/Commands/HealthModels/BaseMonitorHealthModelsCommand.cs @@ -3,8 +3,6 @@ using System.Diagnostics.CodeAnalysis; using Azure.Mcp.Core.Commands; -using Azure.Mcp.Core.Models.Option; -using Azure.Mcp.Tools.Monitor.Commands; using Azure.Mcp.Tools.Monitor.Options; namespace Azure.Mcp.Tools.Monitor.Commands.HealthModels; diff --git a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexGetCommandTests.cs b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexGetCommandTests.cs index 2f196878b4..0b57fd7bb7 100644 --- a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexGetCommandTests.cs @@ -13,7 +13,6 @@ using NSubstitute; using NSubstitute.ExceptionExtensions; using Xunit; -using static Azure.Mcp.Tools.Search.Commands.Index.IndexGetCommand; namespace Azure.Mcp.Tools.Search.UnitTests.Index; From 27bdbe0f00966406c7bd3b43b9dc345bbad8f398 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:04:11 -0700 Subject: [PATCH 02/10] Update inheritance model to allow ServiceStart and other BaseCommand commands to work with BindOptions --- .../Server/Commands/ServiceStartCommand.cs | 173 ++++- .../Areas/Tools/Commands/ToolsListCommand.cs | 2 +- .../src/Commands/BaseCommand.cs | 28 +- .../src/Commands/EmptyOptions.cs | 11 + .../src/Commands/GlobalCommand.cs | 6 +- .../Commands/JsonSourceGenerationContext.cs | 2 +- .../Commands/Runtime/McpRuntimeTests.cs | 14 +- .../CommandFactoryToolLoaderTests.cs | 2 +- .../Areas/Server/ServiceStartCommandTests.cs | 377 +++++++++- eng/vscode/CHANGELOG.md | 680 +++++++++++++++--- servers/Azure.Mcp.Server/CHANGELOG.md | 5 +- .../src/Commands/BestPracticesCommand.cs | 4 +- .../AzureTerraformBestPracticesGetCommand.cs | 2 +- .../Architecture/DiagramGenerateCommand.cs | 4 +- .../Infrastructure/RulesGetCommand.cs | 4 +- .../src/Commands/Plan/GetCommand.cs | 4 +- .../src/Services/KeyVaultService.cs | 4 +- .../src/Services/SqlService.cs | 34 +- .../src/Services/WorkbooksService.cs | 4 +- 19 files changed, 1151 insertions(+), 209 deletions(-) create mode 100644 core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs index 7d55ada957..9d42c419e2 100644 --- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs +++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.CommandLine.Parsing; using System.Net; using Azure.Mcp.Core.Areas.Server.Options; using Azure.Mcp.Core.Commands; @@ -21,7 +22,7 @@ namespace Azure.Mcp.Core.Areas.Server.Commands; /// This command is hidden from the main command list. /// [HiddenCommand] -public sealed class ServiceStartCommand : BaseCommand +public sealed class ServiceStartCommand : BaseCommand { private const string CommandTitle = "Start MCP Server"; @@ -64,57 +65,122 @@ protected override void RegisterOptions(Command command) } /// - /// Executes the service start command, creating and starting the MCP server. + /// Binds the parsed command line arguments to the ServiceStartOptions object. /// - /// The command execution context. - /// The parsed command options. - /// A command response indicating the result of the operation. - public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult) + /// The parsed command line arguments. + /// A configured ServiceStartOptions instance. + protected override ServiceStartOptions BindOptions(ParseResult parseResult) { - string[]? namespaces = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Namespace.Name); - string? mode = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Mode.Name); - bool? readOnly = parseResult.GetValueOrDefault(ServiceOptionDefinitions.ReadOnly.Name); - string? transport = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Transport.Name); + var options = new ServiceStartOptions + { + Transport = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Transport.Name) ?? TransportTypes.StdIo, + Namespace = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Namespace.Name), + Mode = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Mode.Name), + ReadOnly = parseResult.GetValueOrDefault(ServiceOptionDefinitions.ReadOnly.Name), + Debug = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Debug.Name), + EnableInsecureTransports = parseResult.GetValueOrDefault(ServiceOptionDefinitions.EnableInsecureTransports.Name), + InsecureDisableElicitation = parseResult.GetValueOrDefault(ServiceOptionDefinitions.InsecureDisableElicitation.Name) + }; + return options; + } - var debug = parseResult.GetValueOrDefault(ServiceOptionDefinitions.Debug.Name); + /// + /// Validates the command options and arguments. + /// + /// The command result to validate. + /// Optional response object to set error details. + /// A ValidationResult indicating whether the validation passed. + public override ValidationResult Validate(CommandResult commandResult, CommandResponse? commandResponse = null) + { + // First run the base validation for required options and parser errors + var baseResult = base.Validate(commandResult, commandResponse); + if (!baseResult.IsValid) + { + return baseResult; + } + + // Get option values directly from commandResult + var mode = commandResult.GetValueOrDefault(ServiceOptionDefinitions.Mode); + var transport = commandResult.GetValueOrDefault(ServiceOptionDefinitions.Transport); if (!IsValidMode(mode)) { - throw new ArgumentException($"Invalid mode '{mode}'. Valid modes are: {ModeTypes.SingleToolProxy}, {ModeTypes.NamespaceProxy}, {ModeTypes.All}."); + var result = new ValidationResult + { + IsValid = false, + ErrorMessage = $"Invalid mode '{mode}'. Valid modes are: {ModeTypes.SingleToolProxy}, {ModeTypes.NamespaceProxy}, {ModeTypes.All}." + }; + + if (commandResponse != null) + { + commandResponse.Status = 400; + commandResponse.Message = result.ErrorMessage; + } + + return result; } - if (!IsValidTransport(transport)) + try { - throw new ArgumentException($"Invalid transport '{transport}'. Valid transports are: {TransportTypes.StdIo}."); + ValidateTransport(transport); } - - var enableInsecureTransports = parseResult.GetValueOrDefault(ServiceOptionDefinitions.EnableInsecureTransports.Name); - - if (enableInsecureTransports) + catch (ArgumentException ex) { - var includeProdCreds = EnvironmentHelpers.GetEnvironmentVariableAsBool("AZURE_MCP_INCLUDE_PRODUCTION_CREDENTIALS"); - if (!includeProdCreds) + var result = new ValidationResult { - throw new InvalidOperationException("Using --enable-insecure-transport requires the host to have either Managed Identity or Workload Identity enabled. Please refer to the troubleshooting guidelines here at https://aka.ms/azmcp/troubleshooting."); + IsValid = false, + ErrorMessage = ex.Message + }; + + if (commandResponse != null) + { + commandResponse.Status = 400; + commandResponse.Message = result.ErrorMessage; } + + return result; + } + + return new ValidationResult { IsValid = true }; + } + + /// + /// Executes the service start command, creating and starting the MCP server. + /// + /// The command execution context. + /// The parsed command options. + /// A command response indicating the result of the operation. + public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult) + { + if (!Validate(parseResult.CommandResult, context.Response).IsValid) + { + return context.Response; } - var serverOptions = new ServiceStartOptions + var options = BindOptions(parseResult); + + try { - Transport = transport ?? TransportTypes.StdIo, - Namespace = namespaces, - Mode = mode, - ReadOnly = readOnly, - Debug = debug, - EnableInsecureTransports = enableInsecureTransports, - InsecureDisableElicitation = parseResult.GetValueOrDefault(ServiceOptionDefinitions.InsecureDisableElicitation.Name), - }; + if (options.EnableInsecureTransports) + { + var includeProdCreds = EnvironmentHelpers.GetEnvironmentVariableAsBool("AZURE_MCP_INCLUDE_PRODUCTION_CREDENTIALS"); + if (!includeProdCreds) + { + throw new InvalidOperationException("Using --enable-insecure-transport requires the host to have either Managed Identity or Workload Identity enabled. Please refer to the troubleshooting guidelines here at https://aka.ms/azmcp/troubleshooting."); + } + } - using var host = CreateHost(serverOptions); - await host.StartAsync(CancellationToken.None); - await host.WaitForShutdownAsync(CancellationToken.None); + using var host = CreateHost(options); + await host.StartAsync(CancellationToken.None); + await host.WaitForShutdownAsync(CancellationToken.None); - return context.Response; + return context.Response; + } + catch (Exception ex) + { + HandleException(context, ex); + return context.Response; + } } /// @@ -130,15 +196,46 @@ private static bool IsValidMode(string? mode) } /// - /// Validates if the provided transport is a valid transport type. + /// Validates the transport parameter and throws an exception if invalid. /// /// The transport to validate. - /// True if the transport is valid, otherwise false. - private static bool IsValidTransport(string? transport) + /// Thrown when the transport is not null and invalid. + private static void ValidateTransport(string? transport) { - return transport == TransportTypes.StdIo; + if (transport is not null && transport != TransportTypes.StdIo) + { + throw new ArgumentException($"Invalid transport '{transport}'. Valid transports are: {TransportTypes.StdIo}."); + } } + /// + /// Provides custom error messages for service start specific exceptions. + /// + /// The exception to get an error message for. + /// A user-friendly error message. + protected override string GetErrorMessage(Exception ex) => ex switch + { + ArgumentException argEx when argEx.Message.Contains("transport") => + $"Invalid transport option specified. {argEx.Message} Use --transport stdio for standard input/output.", + ArgumentException argEx when argEx.Message.Contains("mode") => + $"Invalid mode option specified. {argEx.Message} Use --mode single, namespace, or all.", + InvalidOperationException opEx when opEx.Message.Contains("enable-insecure-transport") => + "Insecure transport configuration error. Ensure your environment has proper authentication configured (Managed Identity or Workload Identity).", + _ => ex.Message + }; + + /// + /// Provides appropriate HTTP status codes for service start specific exceptions. + /// + /// The exception to get a status code for. + /// An appropriate HTTP status code. + protected override int GetStatusCode(Exception ex) => ex switch + { + ArgumentException => 400, // Bad Request for invalid arguments + InvalidOperationException => 422, // Unprocessable Entity for configuration errors + _ => 500 + }; + /// /// Creates the host for the MCP server with the specified options. /// diff --git a/core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs b/core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs index 02cec3f76a..9890f03276 100644 --- a/core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs +++ b/core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs @@ -8,7 +8,7 @@ namespace Azure.Mcp.Core.Areas.Tools.Commands; [HiddenCommand] -public sealed class ToolsListCommand(ILogger logger) : BaseCommand() +public sealed class ToolsListCommand(ILogger logger) : BaseCommand { private const string CommandTitle = "List Available Tools"; diff --git a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs index f292a9920f..1c9041423d 100644 --- a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs +++ b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs @@ -9,7 +9,7 @@ namespace Azure.Mcp.Core.Commands; -public abstract class BaseCommand : IBaseCommand +public abstract class BaseCommand : IBaseCommand where TOptions : class, new() { private const string MissingRequiredOptionsPrefix = "Missing Required options: "; private const int ValidationErrorStatusCode = 400; @@ -34,6 +34,18 @@ protected virtual void RegisterOptions(Command command) { } + /// + /// Binds the parsed command line arguments to a strongly-typed options object. + /// Override this method in derived classes to provide custom option binding. + /// + /// The parsed command line arguments. + /// An options object containing the bound options. + protected virtual TOptions BindOptions(ParseResult parseResult) + { + // If no specific binding is implemented, create a default instance + return Activator.CreateInstance(); + } + public abstract Task ExecuteAsync(CommandContext context, ParseResult parseResult); protected virtual void HandleException(CommandContext context, Exception ex) @@ -73,11 +85,6 @@ protected virtual void HandleException(CommandContext context, Exception ex) response.Results = ResponseResult.Create(result, JsonSourceGenerationContext.Default.ExceptionResult); } - internal record ExceptionResult( - string Message, - string? StackTrace, - string Type); - protected virtual string GetErrorMessage(Exception ex) => ex.Message; protected virtual int GetStatusCode(Exception ex) => 500; @@ -86,6 +93,7 @@ public virtual ValidationResult Validate(CommandResult commandResult, CommandRes { var result = new ValidationResult { IsValid = true }; + // First, check for missing required options var missingOptions = commandResult.Command.Options .Where(o => o.Required && !o.HasDefaultValue && !commandResult.HasOptionResult(o)) .Select(o => $"--{NameNormalization.NormalizeOptionName(o.Name)}") @@ -101,8 +109,7 @@ public virtual ValidationResult Validate(CommandResult commandResult, CommandRes return result; } - // If no missing required options, propagate parser/validator errors as-is. - // Commands can throw CommandValidationException for structured handling. + // Check for parser/validator errors if (commandResult.Errors != null && commandResult.Errors.Any()) { result.IsValid = false; @@ -124,3 +131,8 @@ static void SetValidationError(CommandResponse? response, string errorMessage) } } } + +internal record ExceptionResult( + string Message, + string? StackTrace, + string Type); diff --git a/core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs b/core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs new file mode 100644 index 0000000000..7601d45a59 --- /dev/null +++ b/core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Azure.Mcp.Core.Commands; + +/// +/// Empty options class for commands that don't need specific options. +/// +public sealed class EmptyOptions +{ +} \ No newline at end of file diff --git a/core/Azure.Mcp.Core/src/Commands/GlobalCommand.cs b/core/Azure.Mcp.Core/src/Commands/GlobalCommand.cs index 4a657e0b4d..07ff177981 100644 --- a/core/Azure.Mcp.Core/src/Commands/GlobalCommand.cs +++ b/core/Azure.Mcp.Core/src/Commands/GlobalCommand.cs @@ -12,7 +12,7 @@ namespace Azure.Mcp.Core.Commands; public abstract class GlobalCommand< - [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions> : BaseCommand + [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions> : BaseCommand where TOptions : GlobalOptions, new() { protected override void RegisterOptions(Command command) @@ -67,7 +67,7 @@ protected virtual string GetCommandPath() return commandPath; } - protected virtual TOptions BindOptions(ParseResult parseResult) + protected override TOptions BindOptions(ParseResult parseResult) { var options = new TOptions { @@ -76,7 +76,7 @@ protected virtual TOptions BindOptions(ParseResult parseResult) }; // Create a RetryPolicyOptions capturing only explicitly provided values so unspecified settings remain SDK defaults - var hasAnyRetry = Azure.Mcp.Core.Options.ParseResultExtensions.HasAnyRetryOptions(parseResult); + var hasAnyRetry = Options.ParseResultExtensions.HasAnyRetryOptions(parseResult); if (hasAnyRetry) { var policy = new RetryPolicyOptions(); diff --git a/core/Azure.Mcp.Core/src/Commands/JsonSourceGenerationContext.cs b/core/Azure.Mcp.Core/src/Commands/JsonSourceGenerationContext.cs index e3207e9c8c..36c1ef73cb 100644 --- a/core/Azure.Mcp.Core/src/Commands/JsonSourceGenerationContext.cs +++ b/core/Azure.Mcp.Core/src/Commands/JsonSourceGenerationContext.cs @@ -7,7 +7,7 @@ namespace Azure.Mcp; -[JsonSerializable(typeof(BaseCommand.ExceptionResult))] +[JsonSerializable(typeof(ExceptionResult))] [JsonSerializable(typeof(JsonElement))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Runtime/McpRuntimeTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Runtime/McpRuntimeTests.cs index ff01e40b49..cb72faefbc 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Runtime/McpRuntimeTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Runtime/McpRuntimeTests.cs @@ -211,7 +211,7 @@ public async Task ListToolsHandler_DelegatesToToolLoader() Assert.Equal(expectedResult, result); await mockToolLoader.Received(1).ListToolsHandler(request, Arg.Any()); - await mockTelemetry.Received(1).StartActivity(TelemetryConstants.ActivityName.ListToolsHandler, Arg.Any()); + await mockTelemetry.Received(1).StartActivity(ActivityName.ListToolsHandler, Arg.Any()); Assert.Equal(ActivityStatusCode.Ok, activity.Status); } @@ -255,7 +255,7 @@ public async Task CallToolHandler_DelegatesToToolLoader() Assert.Equal(expectedResult, result); await mockToolLoader.Received(1).CallToolHandler(request, Arg.Any()); - await mockTelemetry.Received(1).StartActivity(TelemetryConstants.ActivityName.ToolExecuted, Arg.Any()); + await mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any()); Assert.Equal(ActivityStatusCode.Ok, activity.Status); // The runtime may or may not surface telemetry tags on the Activity depending on the @@ -346,10 +346,10 @@ public async Task ListToolsHandler_WhenToolLoaderThrows_PropagatesException() Assert.Equal(expectedException.Message, actualException.Message); - await mockTelemetry.Received(1).StartActivity(TelemetryConstants.ActivityName.ListToolsHandler, Arg.Any()); + await mockTelemetry.Received(1).StartActivity(ActivityName.ListToolsHandler, Arg.Any()); Assert.Equal(ActivityStatusCode.Error, activity.Status); - GetAndAssertTagKeyValue(activity, TelemetryConstants.TagName.ErrorDetails); + GetAndAssertTagKeyValue(activity, TagName.ErrorDetails); } [Fact] @@ -506,9 +506,9 @@ public async Task CallToolHandler_WithNullRequest_ReturnsError() // Verify that the tool loader was NOT called since the null request is handled at the runtime level await mockToolLoader.DidNotReceive().CallToolHandler(Arg.Any>(), Arg.Any()); - await mockTelemetry.Received(1).StartActivity(TelemetryConstants.ActivityName.ToolExecuted, Arg.Any()); + await mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any()); Assert.Equal(ActivityStatusCode.Error, activity.Status); - GetAndAssertTagKeyValue(activity, TelemetryConstants.TagName.ErrorDetails); + GetAndAssertTagKeyValue(activity, TagName.ErrorDetails); } [Fact] @@ -807,7 +807,7 @@ public async Task CallToolHandler_WithToolLoaderError_ShouldReturnErrorAndSetTel // Act var result = await runtime.CallToolHandler(request, CancellationToken.None); - await mockTelemetry.Received(1).StartActivity(TelemetryConstants.ActivityName.ToolExecuted, Arg.Any()); + await mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any()); Assert.Equal(ActivityStatusCode.Error, activity.Status); // Error details are present in the CallToolResult content; assert that instead of relying diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoaderTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoaderTests.cs index 549f85f17e..5d1a394268 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoaderTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoaderTests.cs @@ -62,7 +62,7 @@ public async Task ListToolsHandler_ReturnsToolsWithExpectedProperties() Assert.NotNull(tool.Name); Assert.NotEmpty(tool.Name); Assert.NotNull(tool.Description); - Assert.True(tool.InputSchema.ValueKind != System.Text.Json.JsonValueKind.Null, "InputSchema should not be null"); + Assert.True(tool.InputSchema.ValueKind != JsonValueKind.Null, "InputSchema should not be null"); // Verify this tool corresponds to a command from the factory var correspondingCommand = visibleCommands.FirstOrDefault(kvp => kvp.Key == tool.Name); diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs index 74f3b56a67..1383a88f34 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs @@ -92,19 +92,226 @@ public void AllOptionsRegistered_IncludesInsecureDisableElicitation() [InlineData("websocket")] [InlineData("http")] [InlineData("invalid")] - public async Task ExecuteAsync_InvalidTransport_ThrowsArgumentException(string invalidTransport) + public async Task ExecuteAsync_InvalidTransport_ReturnsValidationError(string invalidTransport) { // Arrange var parseResult = CreateParseResultWithTransport(invalidTransport); var serviceProvider = new ServiceCollection().BuildServiceProvider(); var context = new CommandContext(serviceProvider); - // Act & Assert - var exception = await Assert.ThrowsAsync( - () => _command.ExecuteAsync(context, parseResult)); + // Act + var response = await _command.ExecuteAsync(context, parseResult); + + // Assert + Assert.Equal(400, response.Status); + Assert.Contains($"Invalid transport '{invalidTransport}'", response.Message); + Assert.Contains("Valid transports are: stdio", response.Message); + } + + [Theory] + [InlineData("invalid")] + [InlineData("unknown")] + [InlineData("")] + public async Task ExecuteAsync_InvalidMode_ReturnsValidationError(string invalidMode) + { + // Arrange + var parseResult = CreateParseResultWithMode(invalidMode); + var serviceProvider = new ServiceCollection().BuildServiceProvider(); + var context = new CommandContext(serviceProvider); + + // Act + var response = await _command.ExecuteAsync(context, parseResult); + + // Assert + Assert.Equal(400, response.Status); + Assert.Contains($"Invalid mode '{invalidMode}'", response.Message); + Assert.Contains("Valid modes are: single, namespace, all", response.Message); + } + + [Theory] + [InlineData("single")] + [InlineData("namespace")] + [InlineData("all")] + [InlineData(null)] // null should be valid (uses default) + public async Task ExecuteAsync_ValidMode_DoesNotReturnValidationError(string? validMode) + { + // Arrange + var parseResult = CreateParseResultWithMode(validMode); + var serviceProvider = new ServiceCollection().BuildServiceProvider(); + var context = new CommandContext(serviceProvider); + + // Act + var response = await _command.ExecuteAsync(context, parseResult); + + // Assert - Should not fail validation, though may fail later due to server startup + if (response.Status == 400 && response.Message?.Contains("Invalid mode") == true) + { + Assert.Fail($"Mode '{validMode}' should be valid but got validation error: {response.Message}"); + } + } + + [Fact] + public void BindOptions_WithAllOptions_ReturnsCorrectlyConfiguredOptions() + { + // Arrange + var parseResult = CreateParseResultWithAllOptions(); + + // Act + var options = GetBoundOptions(parseResult); + + // Assert + Assert.Equal("stdio", options.Transport); + Assert.Equal(new[] { "storage", "keyvault" }, options.Namespace); + Assert.Equal("all", options.Mode); + Assert.True(options.ReadOnly); + Assert.True(options.Debug); + Assert.False(options.EnableInsecureTransports); + Assert.True(options.InsecureDisableElicitation); + } + + [Fact] + public void BindOptions_WithDefaults_ReturnsDefaultValues() + { + // Arrange + var parseResult = CreateParseResultWithMinimalOptions(); + + // Act + var options = GetBoundOptions(parseResult); + + // Assert + Assert.Equal("stdio", options.Transport); // Default transport + Assert.Null(options.Namespace); + Assert.Equal("namespace", options.Mode); // Default mode + Assert.False(options.ReadOnly); // Default readonly + Assert.False(options.Debug); + Assert.False(options.EnableInsecureTransports); + Assert.False(options.InsecureDisableElicitation); + } + + [Fact] + public void Validate_WithValidOptions_ReturnsValidResult() + { + // Arrange + var parseResult = CreateParseResultWithTransport("stdio"); + var commandResult = parseResult.CommandResult; + + // Act + var result = _command.Validate(commandResult); + + // Assert + Assert.True(result.IsValid); + Assert.Null(result.ErrorMessage); + } + + [Fact] + public void Validate_WithInvalidTransport_ReturnsInvalidResult() + { + // Arrange + var parseResult = CreateParseResultWithTransport("invalid"); + var commandResult = parseResult.CommandResult; + + // Act + var result = _command.Validate(commandResult); + + // Assert + Assert.False(result.IsValid); + Assert.Contains("Invalid transport 'invalid'", result.ErrorMessage); + } + + [Fact] + public void Validate_WithInvalidMode_ReturnsInvalidResult() + { + // Arrange + var parseResult = CreateParseResultWithMode("invalid"); + var commandResult = parseResult.CommandResult; + + // Act + var result = _command.Validate(commandResult); + + // Assert + Assert.False(result.IsValid); + Assert.Contains("Invalid mode 'invalid'", result.ErrorMessage); + } + + [Fact] + public void GetErrorMessage_WithTransportArgumentException_ReturnsCustomMessage() + { + // Arrange + var exception = new ArgumentException("Invalid transport 'sse'. Valid transports are: stdio."); + + // Act + var message = GetErrorMessage(exception); + + // Assert + Assert.Contains("Invalid transport option specified", message); + Assert.Contains("Use --transport stdio", message); + } + + [Fact] + public void GetErrorMessage_WithModeArgumentException_ReturnsCustomMessage() + { + // Arrange + var exception = new ArgumentException("Invalid mode 'invalid'. Valid modes are: single, namespace, all."); + + // Act + var message = GetErrorMessage(exception); + + // Assert + Assert.Contains("Invalid mode option specified", message); + Assert.Contains("Use --mode single, namespace, or all", message); + } + + [Fact] + public void GetErrorMessage_WithInsecureTransportException_ReturnsCustomMessage() + { + // Arrange + var exception = new InvalidOperationException("Using --enable-insecure-transport requires..."); + + // Act + var message = GetErrorMessage(exception); + + // Assert + Assert.Contains("Insecure transport configuration error", message); + Assert.Contains("proper authentication configured", message); + } + + [Fact] + public void GetStatusCode_WithArgumentException_Returns400() + { + // Arrange + var exception = new ArgumentException("Invalid argument"); + + // Act + var statusCode = GetStatusCode(exception); + + // Assert + Assert.Equal(400, statusCode); + } + + [Fact] + public void GetStatusCode_WithInvalidOperationException_Returns422() + { + // Arrange + var exception = new InvalidOperationException("Invalid operation"); + + // Act + var statusCode = GetStatusCode(exception); - Assert.Contains($"Invalid transport '{invalidTransport}'", exception.Message); - Assert.Contains("Valid transports are: stdio", exception.Message); + // Assert + Assert.Equal(422, statusCode); + } + + [Fact] + public void GetStatusCode_WithGenericException_Returns500() + { + // Arrange + var exception = new Exception("Generic error"); + + // Act + var statusCode = GetStatusCode(exception); + + // Assert + Assert.Equal(500, statusCode); } [Fact] @@ -115,12 +322,44 @@ public async Task ExecuteAsync_ValidTransport_DoesNotThrow() var serviceProvider = new ServiceCollection().BuildServiceProvider(); var context = new CommandContext(serviceProvider); - // Act & Assert - This will throw because the server can't actually start in a unit test, - // but it should not throw an ArgumentException about invalid transport - var exception = await Assert.ThrowsAnyAsync( - () => _command.ExecuteAsync(context, parseResult)); + // Act & Assert - Check that ArgumentException is not thrown for valid transport + try + { + await _command.ExecuteAsync(context, parseResult); + } + catch (ArgumentException ex) when (ex.Message.Contains("transport")) + { + Assert.Fail($"ArgumentException should not be thrown for valid transport: {ex.Message}"); + } + catch + { + // Other exceptions are expected since the server can't actually start in a unit test + // We only care that ArgumentException about transport is not thrown + } + } + + [Fact] + public async Task ExecuteAsync_OmittedTransport_UsesDefaultAndDoesNotThrow() + { + // Arrange + var parseResult = CreateParseResultWithoutTransport(); + var serviceProvider = new ServiceCollection().BuildServiceProvider(); + var context = new CommandContext(serviceProvider); - Assert.IsNotType(exception); + // Act & Assert - Check that ArgumentException is not thrown when transport is omitted + try + { + await _command.ExecuteAsync(context, parseResult); + } + catch (ArgumentException ex) when (ex.Message.Contains("transport")) + { + Assert.Fail($"ArgumentException should not be thrown when transport is omitted (should use default): {ex.Message}"); + } + catch + { + // Other exceptions are expected since the server can't actually start in a unit test + // We only care that ArgumentException about transport is not thrown + } } private static ParseResult CreateParseResult(string? serviceValue) @@ -188,4 +427,120 @@ private static ParseResult CreateParseResultWithTransport(string transport) return root.Parse([.. args]); } + + private static ParseResult CreateParseResultWithoutTransport() + { + var root = new RootCommand + { + ServiceOptionDefinitions.Namespace, + ServiceOptionDefinitions.Transport, + ServiceOptionDefinitions.Mode, + ServiceOptionDefinitions.ReadOnly, + ServiceOptionDefinitions.Debug, + ServiceOptionDefinitions.EnableInsecureTransports, + ServiceOptionDefinitions.InsecureDisableElicitation + }; + var args = new List + { + "--mode", + "all", + "--read-only" + }; + + return root.Parse([.. args]); + } + + private static ParseResult CreateParseResultWithMode(string? mode) + { + var root = new RootCommand + { + ServiceOptionDefinitions.Namespace, + ServiceOptionDefinitions.Transport, + ServiceOptionDefinitions.Mode, + ServiceOptionDefinitions.ReadOnly, + ServiceOptionDefinitions.Debug, + ServiceOptionDefinitions.EnableInsecureTransports, + ServiceOptionDefinitions.InsecureDisableElicitation + }; + var args = new List + { + "--transport", + "stdio" + }; + + if (mode is not null) + { + args.Add("--mode"); + args.Add(mode); + } + + return root.Parse([.. args]); + } + + private static ParseResult CreateParseResultWithAllOptions() + { + var root = new RootCommand + { + ServiceOptionDefinitions.Namespace, + ServiceOptionDefinitions.Transport, + ServiceOptionDefinitions.Mode, + ServiceOptionDefinitions.ReadOnly, + ServiceOptionDefinitions.Debug, + ServiceOptionDefinitions.EnableInsecureTransports, + ServiceOptionDefinitions.InsecureDisableElicitation + }; + var args = new List + { + "--transport", "stdio", + "--namespace", "storage", + "--namespace", "keyvault", + "--mode", "all", + "--read-only", + "--debug", + "--insecure-disable-elicitation" + }; + + return root.Parse([.. args]); + } + + private static ParseResult CreateParseResultWithMinimalOptions() + { + var root = new RootCommand + { + ServiceOptionDefinitions.Namespace, + ServiceOptionDefinitions.Transport, + ServiceOptionDefinitions.Mode, + ServiceOptionDefinitions.ReadOnly, + ServiceOptionDefinitions.Debug, + ServiceOptionDefinitions.EnableInsecureTransports, + ServiceOptionDefinitions.InsecureDisableElicitation + }; + var args = new List(); + + return root.Parse([.. args]); + } + + private ServiceStartOptions GetBoundOptions(ParseResult parseResult) + { + // Use reflection to access the protected BindOptions method + var method = typeof(ServiceStartCommand).GetMethod("BindOptions", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + return (ServiceStartOptions)method!.Invoke(_command, new object[] { parseResult })!; + } + + private string GetErrorMessage(Exception exception) + { + // Use reflection to access the protected GetErrorMessage method + var method = typeof(ServiceStartCommand).GetMethod("GetErrorMessage", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + return (string)method!.Invoke(_command, new object[] { exception })!; + } + + private int GetStatusCode(Exception exception) + { + // Use reflection to access the protected GetStatusCode method + var method = typeof(ServiceStartCommand).GetMethod("GetStatusCode", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + return (int)method!.Invoke(_command, new object[] { exception })!; + } } diff --git a/eng/vscode/CHANGELOG.md b/eng/vscode/CHANGELOG.md index bbbe6285c8..79adfbdf5d 100644 --- a/eng/vscode/CHANGELOG.md +++ b/eng/vscode/CHANGELOG.md @@ -1,17 +1,22 @@ +# CHANGELOG 📝 -# Release History +The Azure MCP Server updates automatically by default whenever a new release comes out 🚀. We ship updates twice a week on Tuesdays and Thursdays 😊 ## 0.8.1 (Unreleased) -### Added +### Features Added -### Changed +### Breaking Changes -### Fixed +### Bugs Fixed + +- Fixed MCP server hanging on invalid transport arguments. Server now exits gracefully with clear error messages instead of hanging indefinitely. [[#311](https://github.com/microsoft/mcp/issues/311)] [[#511](https://github.com/microsoft/mcp/pull/511)] + +### Other Changes ## 0.8.0 (2025-09-18) -### Added +### Features Added - Added the `--insecure-disable-elicitation` server startup switch. When enabled, the server will bypass user confirmation (elicitation) for tools marked as handling secrets and execute them immediately. This is **INSECURE** and meant only for controlled automation scenarios (e.g., CI or disposable test environments) because it removes a safety barrier that helps prevent accidental disclosure of sensitive data. [[#486](https://github.com/microsoft/mcp/pull/486)] - Enhanced Azure authentication with targeted credential selection via the `AZURE_TOKEN_CREDENTIALS` environment variable: [[#56](https://github.com/microsoft/mcp/pull/56)] @@ -21,6 +26,7 @@ - Improved Visual Studio Code credential error handling with proper exception wrapping for credential chaining - Replaced custom `DefaultAzureCredential` implementation with explicit credential chain for better control and transparency - For more details, see [Controlling Authentication Methods with AZURE_TOKEN_CREDENTIALS](https://github.com/microsoft/mcp/blob/main/servers/Azure.Mcp.Server/TROUBLESHOOTING.md#controlling-authentication-methods-with-azure_token_credentials) +- Enhanced AKS nodepool information with comprehensive properties. [[#454](https://github.com/microsoft/mcp/pull/454)] - Added support for updating Azure SQL databases via the command `azmcp_sql_db_update`. [[#488](https://github.com/microsoft/mcp/pull/488)] - Added support for listing Event Grid subscriptions via the command `azmcp_eventgrid_subscription_list`. [[#364](https://github.com/microsoft/mcp/pull/364)] - Added support for listing Application Insights code optimization recommendations across components via the command `azmcp_applicationinsights_recommendation_list`. [#387](https://github.com/microsoft/mcp/pull/387) @@ -30,42 +36,53 @@ - `azmcp_keyvault_key_get` - `azmcp_keyvault_secret_get` -### Changed +### Breaking Changes -- **Breaking:** Redesigned how conditionally required options are handled. Commands now use explicit option registration via extension methods (`.AsRequired()`, `.AsOptional()`) instead of legacy patterns (`UseResourceGroup()`, `RequireResourceGroup()`). [[#452](https://github.com/microsoft/mcp/pull/452)] -- **Breaking:** Removed support for the `AZURE_MCP_INCLUDE_PRODUCTION_CREDENTIALS` environment variable. Use `AZURE_TOKEN_CREDENTIALS` instead for more flexible credential selection. For migration details, see [Controlling Authentication Methods with AZURE_TOKEN_CREDENTIALS](https://github.com/microsoft/mcp/blob/main/servers/Azure.Mcp.Server/TROUBLESHOOTING.md#controlling-authentication-methods-with-azure_token_credentials). [[#56](https://github.com/microsoft/mcp/pull/56)] -- Enhanced AKS nodepool information with comprehensive properties. [[#454](https://github.com/microsoft/mcp/pull/454)] +- Redesigned how conditionally required options are handled. Commands now use explicit option registration via extension methods (`.AsRequired()`, `.AsOptional()`) instead of legacy patterns (`UseResourceGroup()`, `RequireResourceGroup()`). [[#452](https://github.com/microsoft/mcp/pull/452)] +- Removed support for the `AZURE_MCP_INCLUDE_PRODUCTION_CREDENTIALS` environment variable. Use `AZURE_TOKEN_CREDENTIALS` instead for more flexible credential selection. For migration details, see [Controlling Authentication Methods with AZURE_TOKEN_CREDENTIALS](https://github.com/microsoft/mcp/blob/main/servers/Azure.Mcp.Server/TROUBLESHOOTING.md#controlling-authentication-methods-with-azure_token_credentials). [[#56](https://github.com/microsoft/mcp/pull/56)] - Merged `azmcp_appconfig_kv_lock` and `azmcp_appconfig_kv_unlock` into `azmcp_appconfig_kv_lock_set` which can handle locking or unlocking a key-value based on the `--lock` parameter. [[#485](https://github.com/microsoft/mcp/pull/485)] + +### Other Changes + - Update `azmcp_foundry_models_deploy` to use "GenericResource" for deploying models to Azure AI Services. [[#456](https://github.com/microsoft/mcp/pull/456)] +#### Dependency Updates + +- Replaced the `Azure.Bicep.Types.Az` dependency with `Microsoft.Azure.Mcp.AzTypes.Internal.Compact`. [[#472](https://github.com/microsoft/mcp/pull/472)] + ## 0.7.0 (2025-09-16) -### Added +### Features Added -- Added support for diagnosing Azure Resources using the App Lens API via the command `azmcp_applens_resource_diagnose`. [[#356](https://github.com/microsoft/mcp/pull/356)] - Added support for getting a node pool in an AKS managed cluster via the command `azmcp_aks_nodepool_get`. [[#394](https://github.com/microsoft/mcp/pull/394)] +- Added support for diagnosing Azure Resources using the App Lens API via the command `azmcp_applens_resource_diagnose`. [[#356](https://github.com/microsoft/mcp/pull/356)] - Added elicitation support. An elicitation request is sent if the tool annotation `secret` hint is true. [[#404](https://github.com/microsoft/mcp/pull/404)] - Added `azmcp_sql_server_create`, `azmcp_sql_server_delete`, `azmcp_sql_server_show` to support SQL server create, delete, and show commands. [[#312](https://github.com/microsoft/mcp/pull/312)] - Added the support for getting information about Azure Managed Lustre SKUs via the following command `azmcp_azuremanagedlustre_filesystem_get_sku_info`. [[#100](https://github.com/microsoft/mcp/issues/100)] +- Added support for creating and deleting SQL databases via the commands `azmcp_sql_db_create` and `azmcp_sql_db_delete`. [[#434](https://github.com/microsoft/mcp/pull/434)] - `azmcp_functionapp_get` can now list Function Apps on a resource group level. [[#427](https://github.com/microsoft/mcp/pull/427)] -### Changed +### Breaking Changes -- **Breaking:** Merged `azmcp_functionapp_list` into `azmcp_functionapp_get`, which can perform both operations based on whether `--function-app` is passed. [[#427](https://github.com/microsoft/mcp/pull/427)] -- **Breaking:** Removed Azure CLI (`az`) and Azure Developer CLI (`azd`) extension tools to reduce complexity and focus on native Azure service operations. [[#404](https://github.com/microsoft/mcp/pull/404)]. +- Merged `azmcp_functionapp_list` into `azmcp_functionapp_get`, which can perform both operations based on whether `--function-app` is passed. [[#427](https://github.com/microsoft/mcp/pull/427)] +- Removed Azure CLI (`az`) and Azure Developer CLI (`azd`) extension tools to reduce complexity and focus on native Azure service operations. [[#404](https://github.com/microsoft/mcp/pull/404)]. -### Fixed +### Bugs Fixed - Marked the `secret` hint of `azmcp_keyvault_secret_create` tool to "true". [[#430](https://github.com/microsoft/mcp/pull/430)] +### Other Changes + +- Replaced bicep tool dependency on Azure.Bicep.Types.Az package with Microsoft.Azure.Mcp.AzTypes.Internal.Compact package. [[#472](https://github.com/microsoft/mcp/pull/472)] + ## 0.6.0 (2025-09-11) -### Added +### Features Added - **The Azure MCP Server is now also available on NuGet.org** [[#368](https://github.com/microsoft/mcp/pull/368)] -- Added support for listing node pools in an AKS managed cluster. [[#360](https://github.com/microsoft/mcp/pull/360)] +- Added support for listing node pools in an AKS managed cluster via the command `azmcp_aks_nodepool_list`. [[#360](https://github.com/microsoft/mcp/pull/360)] -### Changed +### Breaking Changes - To improve performance, packages now ship with trimmed binaries that have unused code and dependencies removed, resulting in significantly smaller file sizes, faster startup times, and reduced memory footprint. [Learn more](https://learn.microsoft.com/dotnet/core/deploying/trimming/trim-self-contained). [[#405](https://github.com/microsoft/mcp/pull/405)] - Merged `azmcp_search_index_describe` and `azmcp_search_index_list` into `azmcp_search_index_get`, which can perform both operations based on whether `--index` is passed. [[#378](https://github.com/microsoft/mcp/pull/378)] @@ -75,101 +92,136 @@ - `azmcp_storage_blob_container_details` and `azmcp_storage_blob_container_list` into `azmcp_storage_blob_container_get`, which supports the behaviors of both tools based on whether `--container` is passed. - Updated the descriptions of all Storage tools. [[#376](https://github.com/microsoft/mcp/pull/376)] -## 0.5.13 - 2025-09-10 +### Other Changes + +#### Dependency updates + +- Updated the following dependencies: [[#380](https://github.com/microsoft/mcp/pull/380)] + - Azure.Core: `1.47.1` → `1.48.0` + - Azure.Identity: `1.15.0` → `1.16.0` -### Added +## 0.5.13 (2025-09-10) + +### Features Added - Added support for listing all Event Grid topics in a subscription via the command `azmcp_eventgrid_topic_list`. [[#43](https://github.com/microsoft/mcp/pull/43)] - Added support for retrieving knowledge index schema information in Azure AI Foundry projects via the command `azmcp_foundry_knowledge_index_schema`. [[#41](https://github.com/microsoft/mcp/pull/41)] - Added support for listing service health events in a subscription via the command `azmcp_resourcehealth_service-health-events_list`. [[#367](https://github.com/microsoft/mcp/pull/367)] -### Changed +### Breaking Changes -- **Breaking:** Updated/removed options for the following commands: [[#108](https://github.com/microsoft/mcp/pull/108)] +- Updated/removed options for the following commands: [[#108](https://github.com/microsoft/mcp/pull/108)] - `azmcp_storage_account_create`: Removed the ability to configure `enable-https-traffic-only` (always `true` now), `allow-blob-public-access` (always `false` now), and `kind` (always `StorageV2` now). - `azmcp_storage_blob_container_create`: Removed the ability to configure `blob-container-public-access` (always `false` now). - `azmcp_storage_blob_upload`: Removed the ability to configure `overwrite` (always `false` now). + +### Bugs Fixed + +- Fixed telemetry bug where "ToolArea" was incorrectly populated in with "ToolName". [[#346](https://github.com/microsoft/mcp/pull/346)] + +### Other Changes + - Added telemetry to log parameter values for the `azmcp_bestpractices_get` tool. [[#375](https://github.com/microsoft/mcp/pull/375)] - Updated tool annotations. [[#377](https://github.com/microsoft/mcp/pull/377)] -### Fixed +#### Dependency updates -- Fixed telemetry bug where "ToolArea" was incorrectly populated with "ToolName". [[#346](https://github.com/microsoft/mcp/pull/346)] +- Updated the following dependencies: + - Azure.Identity: `1.14.0` → `1.15.0` [[#352](https://github.com/microsoft/mcp/pull/352)] + - Azure.Identity.Broker: `1.2.0` → `1.3.0` [[#352](https://github.com/microsoft/mcp/pull/352)] + - Microsoft.Azure.Cosmos.Aot: `0.1.1-preview.1` → `0.1.2-preview.1` [[#383](https://github.com/microsoft/mcp/pull/383)] +- Updated the following dependency to improve .NET Ahead-of-Time (AOT) compilation support: [[#363](https://github.com/microsoft/mcp/pull/363)] + - Azure.ResourceManager.StorageCache: `1.3.1` → `1.3.2` -## 0.5.12 - 2025-09-04 +## 0.5.12 (2025-09-04) -### Added +### Features Added - Added `azmcp_sql_server_firewall-rule_create` and `azmcp_sql_server_firewall-rule_delete` commands. [[#121](https://github.com/microsoft/mcp/pull/121)] -- Added a verb to the namespace name for bestpractices. [[#109](https://github.com/microsoft/mcp/pull/109)] -- Added instructions about consumption plan for azure functions deployment best practices. [[#218](https://github.com/microsoft/mcp/pull/218)] -### Fixed +### Bugs Fixed - Fixed a bug in MySQL query validation logic. [[#81](https://github.com/microsoft/mcp/pull/81)] -## 0.5.11 - 2025-09-02 +### Other Changes + +AOT- Added a verb to the namespace name for bestpractices [[#109](https://github.com/microsoft/mcp/pull/109)] +- Added instructions about consumption plan for azure functions deployment best practices [[#218](https://github.com/microsoft/mcp/pull/218)] -### Fixed +## 0.5.11 (2025-09-02) + +### Other Changes - Fixed VSIX signing [[#91](https://github.com/microsoft/mcp/pull/91)] - Included native packages in build artifacts and pack/release scripts. [[#51](https://github.com/microsoft/mcp/pull/51)] -## 0.5.10 - 2025-08-28 +## 0.5.10 (2025-08-28) -### Fixed +### Bugs fixed - Fixed a bug with telemetry collection related to AppConfig tools. [[#44](https://github.com/microsoft/mcp/pull/44)] -## 0.5.9 - 2025-08-26 +## 0.5.9 (2025-08-26) + +### Other Changes -### Changed +#### Dependency Updates -- Updated dependencies to improve .NET Ahead-of-Time (AOT) compilation support: - - `Microsoft.Azure.Cosmos` `3.51.0` → `Microsoft.Azure.Cosmos.Aot` `0.1.1-preview.1`. [[#37](https://github.com/microsoft/mcp/pull/37)] +- Updated the following dependencies to improve .NET Ahead-of-Time (AOT) compilation support: + - Microsoft.Azure.Cosmos `3.51.0` → Microsoft.Azure.Cosmos.Aot `0.1.1-preview.1`. [[#37](https://github.com/microsoft/mcp/pull/37)] -## 0.5.8 - 2025-08-21 +## 0.5.8 (2025-08-21) -### Added +### Features Added - Added support for listing knowledge indexes in Azure AI Foundry projects via the command `azmcp_foundry_knowledge_index_list`. [[#1004](https://github.com/Azure/azure-mcp/pull/1004)] -- Added support for getting details of an Azure Function App via the `azmcp_functionapp_get` command. [[#970](https://github.com/Azure/azure-mcp/pull/970)] +- Added support for getting details of an Azure Function App via the command `azmcp_functionapp_get`. [[#970](https://github.com/Azure/azure-mcp/pull/970)] - Added the following Azure Managed Lustre commands: [[#1003](https://github.com/Azure/azure-mcp/issues/1003)] - `azmcp_azuremanagedlustre_filesystem_list`: List available Azure Managed Lustre filesystems. - `azmcp_azuremanagedlustre_filesystem_required-subnet-size`: Returns the number of IP addresses required for a specific SKU and size of Azure Managed Lustre filesystem. -- Added support for designing Azure Cloud Architecture through guided questions via the `azmcp_cloudarchitect_design` command. [[#890](https://github.com/Azure/azure-mcp/pull/890)] +- Added support for designing Azure Cloud Architecture through guided questions via the command `azmcp_cloudarchitect_design`. [[#890](https://github.com/Azure/azure-mcp/pull/890)] - Added support for the following Azure MySQL operations: [[#855](https://github.com/Azure/azure-mcp/issues/855)] - `azmcp_mysql_database_list` - List all databases in a MySQL server. - - `azmcp_mysql_database_query` - Execute a SELECT query on a MySQL database (non-destructive only). + - `azmcp_mysql_database_query` - Executes a SELECT query on a MySQL Database. The query must start with SELECT and cannot contain any destructive SQL operations for security reasons. - `azmcp_mysql_table_list` - List all tables in a MySQL database. - `azmcp_mysql_table_schema_get` - Get the schema of a specific table in a MySQL database. - `azmcp_mysql_server_config_get` - Retrieve the configuration of a MySQL server. - - `azmcp_mysql_server_list` - List all MySQL servers in a subscription and resource group. + - `azmcp_mysql_server_list` - List all MySQL servers in a subscription & resource group. - `azmcp_mysql_server_param_get` - Retrieve a specific parameter of a MySQL server. - `azmcp_mysql_server_param_set` - Set a specific parameter of a MySQL server to a specific value. - Added telemetry for tracking service area when calling tools. [[#1024](https://github.com/Azure/azure-mcp/pull/1024)] -### Changed +### Breaking Changes -- Standardized Azure Storage command descriptions, option names, and parameter names; cleaned up JSON serialization context. [[#1015](https://github.com/Azure/azure-mcp/pull/1015)] - - **Breaking:** Renamed the following Storage tool option names for consistency: - - `azmcp_storage_account_create`: `account-name` → `account`. - - `azmcp_storage_blob_batch_set-tier`: `blob-names` → `blobs`. -- Introduced `BaseAzureResourceService` to enable Azure Resource read operations using Azure Resource Graph queries. [[#938](https://github.com/Azure/azure-mcp/pull/938)] -- Refactored SQL service to use Azure Resource Graph instead of direct ARM API calls, removing dependency on `Azure.ResourceManager.Sql` and improving startup performance. [[#938](https://github.com/Azure/azure-mcp/pull/938)] -- Enhanced `BaseAzureService` with `EscapeKqlString` for safe KQL query construction across all Azure services; fixed KQL string escaping in Workbooks queries. [[#938](https://github.com/Azure/azure-mcp/pull/938)] -- Updated to .NET 10 SDK to prepare for .NET tool packing. -- Improved `bestpractices` and `azureterraformbestpractices` tool descriptions to work better with VS Code Copilot tool grouping. [[#1029](https://github.com/Azure/azure-mcp/pull/1029)] +- Renamed the following Storage tool option names: [[#1015](https://github.com/Azure/azure-mcp/pull/1015)] + - `azmcp_storage_account_create`: `account-name` → `account`. + - `azmcp_storage_blob_batch_set-tier`: `blob-names` → `blobs`. -### Fixed +### Bugs Fixed -- SQL service tests now use case-insensitive string comparisons for resource type validation. [[#938](https://github.com/Azure/azure-mcp/pull/938)] -- HttpClient service tests now validate NoProxy collection handling correctly (instead of assuming a single string). [[#938](https://github.com/Azure/azure-mcp/pull/938)] +- Fixed SQL service test assertions to use case-insensitive string comparisons for resource type validation. [[#938](https://github.com/Azure/azure-mcp/pull/938)] +- Fixed HttpClient service test assertions to properly validate NoProxy collection handling instead of expecting a single string value. [[#938](https://github.com/Azure/azure-mcp/pull/938)] -## 0.5.7 - 2025-08-19 +### Other Changes -### Added +- Introduced the `BaseAzureResourceService` class to allow performing Azure Resource read operations using Azure Resource Graph queries. [[#938](https://github.com/Azure/azure-mcp/pull/938)] +- Refactored SQL service implementation to use Azure Resource Graph queries instead of direct ARM API calls. [[#938](https://github.com/Azure/azure-mcp/pull/938)] + - Removed dependency on `Azure.ResourceManager.Sql` package by migrating to Azure Resource Graph queries, reducing package size and improving startup performance. +- Enhanced `BaseAzureService` with `EscapeKqlString` method for safe KQL query construction across all Azure services. [[#938](https://github.com/Azure/azure-mcp/pull/938)] + - Fixed KQL string escaping in Workbooks service queries. +- Standardized Azure Storage command descriptions, option names, and parameter names for consistency across all storage commands. Updated JSON serialization context to remove unused model types and improve organization. [[#1015](https://github.com/Azure/azure-mcp/pull/1015)] +- Updated to .NET 10 SDK to prepare for .NET tool packing. [[#1023](https://github.com/Azure/azure-mcp/pull/1023)] +- Enhanced `bestpractices` and `azureterraformbestpractices` tool descriptions to better work with the vscode copilot tool grouping feature. [[#1029](https://github.com/Azure/azure-mcp/pull/1029)] +- The Azure MCP Server can now be packaged as a .NET SDK Tool for easier use by users with the .NET 10 SDK installed. [[#422](https://github.com/Azure/azure-mcp/issues/422)] +#### Dependency Updates + +- Updated the following dependencies to improve .NET Ahead-of-Time (AOT) compilation support: [[#1031](https://github.com/Azure/azure-mcp/pull/1031)] + - Azure.ResourceManager.ResourceHealth: `1.0.0` → `1.1.0-beta.5` + +## 0.5.7 (2025-08-19) + +### Features Added - Added support for the following Azure Deploy and Azure Quota operations: [[#626](https://github.com/Azure/azure-mcp/pull/626)] - `azmcp_deploy_app_logs_get` - Get logs from Azure applications deployed using azd. - `azmcp_deploy_iac_rules_get` - Get Infrastructure as Code rules. @@ -178,72 +230,87 @@ - `azmcp_deploy_architecture_diagram-generate` - Generate Azure service architecture diagrams based on application topology. - `azmcp_quota_region_availability-list` - List available Azure regions for specific resource types. - `azmcp_quota_usage_check` - Check Azure resource usage and quota information for specific resource types and regions. -- Added support for listing Azure Function Apps via the `azmcp-functionapp-list` command. [[#863](https://github.com/Azure/azure-mcp/pull/863)] -- Added support for importing existing certificates into Azure Key Vault via the `azmcp-keyvault-certificate-import` command. [[#968](https://github.com/Azure/azure-mcp/issues/968)] -- Added support for uploading a local file to an Azure Storage blob via the `azmcp-storage-blob-upload` command. [[#960](https://github.com/Azure/azure-mcp/pull/960)] +- Added support for listing Azure Function Apps via the command `azmcp-functionapp-list`. [[#863](https://github.com/Azure/azure-mcp/pull/863)] +- Added support for importing existing certificates into Azure Key Vault via the command `azmcp-keyvault-certificate-import`. [[#968](https://github.com/Azure/azure-mcp/issues/968)] +- Added support for uploading a local file to an Azure Storage blob via the command `azmcp-storage-blob-upload`. [[#960](https://github.com/Azure/azure-mcp/pull/960)] - Added support for the following Azure Service Health operations: [[#998](https://github.com/Azure/azure-mcp/pull/998)] - `azmcp-resourcehealth-availability-status-get` - Get the availability status for a specific resource. - `azmcp-resourcehealth-availability-status-list` - List availability statuses for all resources in a subscription or resource group. -- Added support for listing repositories in Azure Container Registries via the `azmcp-acr-registry-repository-list` command. [[#983](https://github.com/Azure/azure-mcp/pull/983)] +- Added support for listing repositories in Azure Container Registries via the command `azmcp-acr-registry-repository-list`. [[#983](https://github.com/Azure/azure-mcp/pull/983)] -### Changed +### Other Changes - Improved guidance for LLM interactions with Azure MCP server by adding rules around bestpractices tool calling to server instructions. [[#1007](https://github.com/Azure/azure-mcp/pull/1007)] -## 0.5.6 - 2025-08-14 +#### Dependency Updates + +- Updated the following dependencies to improve .NET Ahead-of-Time (AOT) compilation support: [[#893](https://github.com/Azure/azure-mcp/pull/893)] + - Azure.Bicep.Types: `0.5.110` → `0.6.1` + - Azure.Bicep.Types.Az: `0.2.771` → `0.2.792` +- Added the following dependencies to support Azure Managed Lustre + - Azure.ResourceManager.StorageCache: `1.3.1` + +## 0.5.6 (2025-08-14) + +### Features Added -### Added +- Added support for listing Azure Function Apps via the command `azmcp-functionapp-list`. [[#863](https://github.com/Azure/azure-mcp/pull/863)] +- Added support for getting details about an Azure Storage Account via the command `azmcp-storage-account-details`. [[#934](https://github.com/Azure/azure-mcp/issues/934)] -- New VS Code settings to control Azure MCP server startup behavior: [[#971](https://github.com/Azure/azure-mcp/issues/971)] - - `azureMcp.serverMode`: choose tool exposure mode — `single` | `namespace` (default) | `all`. - - `azureMcp.readOnly`: start the server in read-only mode. - - `azureMcp.enabledServices`: added drop down list to select and configure the enabled services. -- Added support for listing Azure Function Apps via the `azmcp-functionapp-list` command. [[#863](https://github.com/Azure/azure-mcp/pull/863)] -- Added support for getting details about an Azure Storage Account via the `azmcp-storage-account-details` command. [[#934](https://github.com/Azure/azure-mcp/issues/934)] +### Other Changes -### Changed +- Refactored resource group option (`--resource-group`) handling and validation for all commands to a centralized location. [[#961](https://github.com/Azure/azure-mcp/issues/961)] -- Centralized handling and validation of the `--resource-group` option across all commands. [[#961](https://github.com/Azure/azure-mcp/issues/961)] +#### Dependency Updates -## 0.5.5 - 2025-08-12 +- Updated the following dependencies to improve .NET Ahead-of-Time (AOT) compilation support: [[#967](https://github.com/Azure/azure-mcp/issues/967)] [[#969](https://github.com/Azure/azure-mcp/issues/969)] + - Azure.Monitor.Query: `1.6.0` → `1.7.1` + - Azure.Monitor.Ingestion: `1.1.2` → `1.2.0` + - Azure.Search.Documents: `11.7.0-beta.4` → `11.7.0-beta.6` + - Azure.ResourceManager.ContainerRegistry: `1.3.0` → `1.3.1` + - Azure.ResourceManager.DesktopVirtualization: `1.3.1` → `1.3.2` + - Azure.ResourceManager.PostgreSql: `1.3.0` → `1.3.1` -### Added +## 0.5.5 (2025-08-12) -- Added support for listing Azure Container Registry (ACR) registries in a subscription via the `azmcp-acr-registry-list` command. [[#915](https://github.com/Azure/azure-mcp/issues/915)] -- Added new Azure Storage commands: - - `azmcp-storage-account-create`: Create a new Storage account. [[#927](https://github.com/Azure/azure-mcp/issues/927)] - - `azmcp-storage-queue-message-send`: Send a message to a Storage queue. [[#794](https://github.com/Azure/azure-mcp/pull/794)] - - `azmcp-storage-blob-details`: Get details about a Storage blob. [[#930](https://github.com/Azure/azure-mcp/issues/930)] - - `azmcp-storage-blob-container-create`: Create a new Storage blob container. [[#937](https://github.com/Azure/azure-mcp/issues/937)] -- Bundled the **GitHub Copilot for Azure** extension as part of the Azure MCP Server extension pack. +### Features Added -### Changed +- Added support for listing ACR (Azure Container Registry) registries in a subscription via the command `azmcp-acr-registry-list`. [[#915](https://github.com/Azure/azure-mcp/issues/915)] +- Added the following Azure Storage commands: + - `azmcp-storage-account-create`: Create a new Azure Storage account. [[#927](https://github.com/Azure/azure-mcp/issues/927)] + - `azmcp-storage-queue-message-send`: Send a message to an Azure Storage queue. [[#794](https://github.com/Azure/azure-mcp/pull/794)] + - `azmcp-storage-blob-details`: Get details about an Azure Storage blob. [[#930](https://github.com/Azure/azure-mcp/issues/930)] + - `azmcp-storage-blob-container-create`: Create a new Azure Storage blob container. [[#937](https://github.com/Azure/azure-mcp/issues/937)] + +### Breaking Changes - The `azmcp-storage-account-list` command now returns account metadata objects instead of plain strings. Each item includes: `name`, `location`, `kind`, `skuName`, `skuTier`, `hnsEnabled`, `allowBlobPublicAccess`, `enableHttpsTrafficOnly`. Update scripts to read the `name` property. The underlying `IStorageService.GetStorageAccounts()` signature changed from `Task>` to `Task>`. [[#904](https://github.com/Azure/azure-mcp/issues/904)] -- Consolidated "AzSubscriptionGuid" telemetry logic into `McpRuntime`. [[#935](https://github.com/Azure/azure-mcp/pull/935)] -### Fixed +### Bugs Fixed - Fixed best practices tool invocation failure when passing "all" action with "general" or "azurefunctions" resources. [[#757](https://github.com/Azure/azure-mcp/issues/757)] - Updated metadata for CREATE and SET tools to `destructive = true`. [[#773](https://github.com/Azure/azure-mcp/pull/773)] -## 0.5.4 - 2025-08-07 +### Other Changes +- Consolidate "AzSubscriptionGuid" telemetry logic into `McpRuntime`. [[#935](https://github.com/Azure/azure-mcp/pull/935)] -### Changed +## 0.5.4 (2025-08-07) -- Improved Azure MCP display name in VS Code from 'azure-mcp-server-ext' to 'Azure MCP' for better user experience in the Configure Tools interface. [[#871](https://github.com/Azure/azure-mcp/issues/871), [#876](https://github.com/Azure/azure-mcp/pull/876)] -- Updated the description of the following `CommandGroup`s to improve their tool usage by Agents: - - Azure AI Search [[#874](https://github.com/Azure/azure-mcp/pull/874)] - - Storage [#879](https://github.com/Azure/azure-mcp/pull/879) - -### Fixed +### Bugs Fixed - Fixed subscription parameter handling across all Azure MCP service methods to consistently use `subscription` instead of `subscriptionId`, enabling proper support for both subscription IDs and subscription names. [[#877](https://github.com/Azure/azure-mcp/issues/877)] - Fixed `ToolExecuted` telemetry activity being created twice. [[#741](https://github.com/Azure/azure-mcp/pull/741)] -## 0.5.3 - 2025-08-05 +### Other Changes + +- Improved Azure MCP display name in VS Code from 'azure-mcp-server-ext' to 'Azure MCP' for better user experience in the Configure Tools interface. [[#871](https://github.com/Azure/azure-mcp/issues/871), [#876](https://github.com/Azure/azure-mcp/pull/876)] +- Updated the following `CommandGroup` descriptions to improve their tool usage by Agents: + - Azure AI Search [[#874](https://github.com/Azure/azure-mcp/pull/874)] + - Storage [[#879](https://github.com/Azure/azure-mcp/pull/879)] + +## 0.5.3 (2025-08-05) -### Added +### Features Added - Added support for providing the `--content-type` and `--tags` properties to the `azmcp-appconfig-kv-set` command. [[#459](https://github.com/Azure/azure-mcp/pull/459)] - Added `filter-path` and `recursive` capabilities to `azmcp-storage-datalake-file-system-list-paths`. [[#770](https://github.com/Azure/azure-mcp/issues/770)] @@ -253,9 +320,8 @@ - `azmcp-virtualdesktop-sessionhost-list` - List all session hosts in a host pool - `azmcp-virtualdesktop-sessionhost-usersession-list` - List all user sessions on a specific session host - Added support for creating and publishing DevDeviceId in telemetry. [[#810](https://github.com/Azure/azure-mcp/pull/810/)] -- Added caching for Cosmos DB databases and containers. [[813](https://github.com/Azure/azure-mcp/pull/813)] -### Changed +### Breaking Changes - **Parameter Name Changes**: Removed unnecessary "-name" suffixes from command parameters across 25+ parameters in 12+ Azure service areas to improve consistency and usability. Users will need to update their command-line usage and scripts. [[#853](https://github.com/Azure/azure-mcp/pull/853)] - **AppConfig**: `--account-name` → `--account` @@ -269,30 +335,428 @@ - **Monitor**: `--table-name` → `--table`, `--model` → `--health-model`, `--resource-name` → `--resource` - **Foundry**: `--deployment-name` → `--deployment`, `--publisher-name` → `--publisher`, `--license-name` → `--license`, `--sku-name` → `--sku`, `--azure-ai-services-name` → `--azure-ai-services` -### Fixed +### Bugs Fixed - Fixed an issue where the `azmcp-storage-blob-batch-set-tier` command did not correctly handle the `--tier` parameter when setting the access tier for multiple blobs. [[#808](https://github.com/Azure/azure-mcp/pull/808)] -## 0.5.2 - 2025-07-31 - -### Added +### Other Changes + +- Implemented centralized HttpClient service with proxy support for better resource management and enterprise compatibility. [[#857](https://github.com/Azure/azure-mcp/pull/857)] +- Added caching for Cosmos DB databases and containers. [[#813](https://github.com/Azure/azure-mcp/pull/813)] +- Refactored PostgreSQL commands to follow ObjectVerb naming pattern, fix command hierarchy, and ensure all commands end with verbs. This improves consistency and discoverability across all postgres commands. [[#865](https://github.com/Azure/azure-mcp/issues/865)] [[#866](https://github.com/Azure/azure-mcp/pull/866)] + +#### Dependency Updates + +- Updated the following dependencies to improve .NET Ahead-of-Time (AOT) compilation support. AOT will enable shipping Azure MCP Server as self-contained native executable. + - Azure.Core: `1.46.2` → `1.47.1` + - Azure.ResourceManager: `1.13.1` → `1.13.2` + - Azure.ResourceManager.ApplicationInsights: `1.0.1` → `1.1.0-beta.1` + - Azure.ResourceManager.AppConfiguration: `1.4.0` → `1.4.1` + - Azure.ResourceManager.Authorization: `1.1.4` → `1.1.5` + - Azure.ResourceManager.ContainerService: `1.2.3` → `1.2.5` + - Azure.ResourceManager.Kusto: `1.6.0` → `1.6.1` + - Azure.ResourceManager.CognitiveServices: `1.4.0` → `1.5.1` + - Azure.ResourceManager.Redis: `1.5.0` → `1.5.1` + - Azure.ResourceManager.RedisEnterprise: `1.1.0` → `1.2.1` + - Azure.ResourceManager.LoadTesting: `1.1.1` → `1.1.2` + - Azure.ResourceManager.Sql: `1.3.0` → `1.4.0-beta.3` + - Azure.ResourceManager.Datadog: `1.0.0-beta.5` → `1.0.0-beta.6` + - Azure.ResourceManager.CosmosDB: `1.3.2` → `1.4.0-beta.13` + - Azure.ResourceManager.OperationalInsights: `1.3.0` → `1.3.1` + - Azure.ResourceManager.Search: `1.2.3` → `1.3.0` + - Azure.ResourceManager.Storage: `1.4.2` → `1.4.4` + - Azure.ResourceManager.Grafana: `1.1.1` → `1.2.0-beta.2` + - Azure.ResourceManager.ResourceGraph: `1.1.0-beta.3` → `1.1.0-beta.4` + +## 0.5.2 (2025-07-31) + +### Features Added - Added support for batch setting access tier for multiple Azure Storage blobs via the `azmcp-storage-blob-batch-set-tier` command. This command efficiently changes the storage tier (Hot, Cool, Archive, etc) for multiple blobs simultaneously in a single operation. [[#735](https://github.com/Azure/azure-mcp/issues/735)] - Added descriptions to all Azure MCP command groups to improve discoverability and usability when running the server with `--mode single` or `--mode namespace`. [[#791](https://github.com/Azure/azure-mcp/pull/791)] -### Changed +### Breaking Changes -- Removed toast notifications related to Azure MCP server registration and startup instructions.[[#785](https://github.com/Azure/azure-mcp/pull/785)] - Removed `--partner-tenant-id` option from `azmcp-marketplace-product-get` command. [[#656](https://github.com/Azure/azure-mcp/pull/656)] -## 0.5.1 - 2025-07-29 +## 0.5.1 (2025-07-29) -### Added +### Features Added - Added support for listing SQL databases via the command: `azmcp-sql-db-list`. [[#746](https://github.com/Azure/azure-mcp/pull/746)] - Added support for reading `AZURE_SUBSCRIPTION_ID` from the environment variables if a subscription is not provided. [[#533](https://github.com/Azure/azure-mcp/pull/533)] -## 0.5.0 - 2025-07-24 +### Breaking Changes + +- Removed the following Key Vault operations: [[#768](https://github.com/Azure/azure-mcp/pull/768)] + - `azmcp-keyvault-secret-get` + - `azmcp-keyvault-key-get` + +### Other Changes + +- Improved the MAC address search logic for telemetry by making it more robust in finding a valid network interface. [[#759](https://github.com/Azure/azure-mcp/pull/759)] +- Major repository structure change: + - Service areas moved from `/src/areas/{Area}` and `/tests/areas/{Area}` into `/areas/{area}/src` and `/areas/{area}/tests` + - Common code moved into `/core/src` and `/core/tests` + +## 0.5.0 (2025-07-24) + +### Features Added + +- Added a new VS Code extension (VSIX installer) for the VS Code Marketplace. [[#661](https://github.com/Azure/azure-mcp/pull/661)] +- Added `--mode all` startup option to expose all Azure MCP tools individually. [[#689](https://github.com/Azure/azure-mcp/issues/689)] +- Added more tools for Azure Key Vault: [[#517](https://github.com/Azure/azure-mcp/pull/517)] + - `azmcp-keyvault-certificate-list` - List certificates in a key vault + - `azmcp-keyvault-certificate-get` - Get details of a specific certificate + - `azmcp-keyvault-certificate-create` - Create a new certificate + - `azmcp-keyvault-secret-list` - List secrets in a key vault + - `azmcp-keyvault-secret-create` - Create a new secret +- Added support for Azure Workbooks management operations: [[#629](https://github.com/Azure/azure-mcp/pull/629)] + - `azmcp-workbooks-list` - List workbooks in a resource group with optional filtering + - `azmcp-workbooks-show` - Get detailed information about a specific workbook + - `azmcp-workbooks-create` - Create new workbooks with custom visualizations and content + - `azmcp-workbooks-update` - Update existing workbook configurations and metadata + - `azmcp-workbooks-delete` - Delete workbooks when no longer needed +- Added support for creating a directory in Azure Storage DataLake via the `azmcp-storage-datalake-directory-create` command. [[#647](https://github.com/Azure/azure-mcp/pull/647)] +- Added support for getting the details of an Azure Kubernetes Service (AKS) cluster via the `azmcp-aks-cluster-get` command. [[#700](https://github.com/Azure/azure-mcp/pull/700)] + +### Breaking Changes + +- Changed the default startup mode to list tools at the namespace level instead of at an individual level, reducing total tool count from around 128 tools to 25. Use `--mode all` to restore the previous behavior of exposing all tools individually. [[#689](https://github.com/Azure/azure-mcp/issues/689)] +- Consolidated Azure best practices commands into the command `azmcp-bestpractices-get` with `--resource` and `--action` parameters: [[#677](https://github.com/Azure/azure-mcp/pull/677)] + - Removed `azmcp-bestpractices-general-get`, `azmcp-bestpractices-azurefunctions-get-code-generation` and `azmcp-bestpractices-azurefunctions-get-deployment` + - Use `--resource general --action code-generation` for general Azure code generation best practices + - Use `--resource general --action deployment` for general Azure deployment best practices + - Use `--resource azurefunctions --action code-generation` instead of the old azurefunctions code-generation command + - Use `--resource azurefunctions --action deployment` instead of the old azurefunctions deployment command + - Use `--resource static-web-app --action all` to get Static Web Apps development and deployment best practices + +### Bugs Fixed + +- Fixes tool discovery race condition causing "tool not found" errors in MCP clients that use different processes to start and use the server, like LangGraph. [[#556](https://github.com/Azure/azure-mcp/issues/556)] + +## 0.4.1 (2025-07-17) + +### Features Added + +- Added support for the following Azure Load Testing operations: [[#315](https://github.com/Azure/azure-mcp/pull/315)] + - `azmcp-loadtesting-testresource-list` - List Azure Load testing resources. + - `azmcp-loadtesting-testresource-create` - Create a new Azure Load testing resource. + - `azmcp-loadtesting-test-get` - Get details of a specific load test configuration. + - `azmcp-loadtesting-test-create` - Create a new load test configuration. + - `azmcp-loadtesting-testrun-get` - Get details of a specific load test run. + - `azmcp-loadtesting-testrun-list` - List all load test runs for a specific test. + - `azmcp-loadtesting-testrun-create` - Create a new load test run. + - `azmcp-loadtesting-testrun-delete` - Delete a specific load test run. +- Added support for scanning Azure resources for compliance recommendations using the Azure Quick Review CLI via the command: `azmcp-extension-azqr`. [[#510](https://github.com/Azure/azure-mcp/pull/510)] +- Added support for listing paths in Data Lake file systems via the command: `azmcp-storage-datalake-file-system-list-paths`. [[#608](https://github.com/Azure/azure-mcp/pull/608)] +- Added support for listing SQL elastic pools via the command: `azmcp-sql-elastic-pool-list`. [[#606](https://github.com/Azure/azure-mcp/pull/606)] +- Added support for listing SQL server firewall rules via the command: `azmcp-sql-firewall-rule-list`. [[#610](https://github.com/Azure/azure-mcp/pull/610)] +- Added new commands for obtaining Azure Functions best practices via the following commands: [[#630](https://github.com/Azure/azure-mcp/pull/630)] + - `azmcp-bestpractices-azurefunctions-get-code-generation` - Get code generation best practices for Azure Functions. + - `azmcp-bestpractices-azurefunctions-get-deployment` - Get deployment best practices for Azure Functions. +- Added support for get details about a product in the Azure Marketplace via the command: `azmcp-marketplace-product-get`. [[#442](https://github.com/Azure/azure-mcp/pull/442)] + +### Breaking Changes + +- Renamed the command `azmcp-bestpractices-get` to `azmcp-bestpractices-general-get`. [[#630](https://github.com/Azure/azure-mcp/pull/630)] + +### Bugs Fixed + +- Fixed an issue with Azure CLI executable path resolution on Windows. [[#611](https://github.com/Azure/azure-mcp/issues/611)] +- Fixed a tool discovery timing issue when calling tools on fresh server instances. [[#604](https://github.com/Azure/azure-mcp/issues/604)] +- Fixed issue where unrecognizable json would be sent to MCP clients in STDIO mode at startup. [[#644](https://github.com/Azure/azure-mcp/issues/644)] + +### Other Changes + +- Changed `engines.node` in `package.json` to require Node.js version `>=20.0.0`. [[#628](https://github.com/Azure/azure-mcp/pull/628)] + +## 0.4.0 (2025-07-15) + +### Features Added + +- Added support for listing Azure Kubernetes Service (AKS) clusters via the command `azmcp-aks-cluster-list`. [[#560](https://github.com/Azure/azure-mcp/pull/560)] +- Made the following Ahead of Time (AOT) compilation improvements saving `6.96 MB` in size total: + - Switched to the trimmer-friendly `CreateSlimBuilder` API from `CreateBuilder`, saving `0.63 MB` in size for the native executable. [[#564](https://github.com/Azure/azure-mcp/pull/564)] + - Switched to the trimmer-friendly `npgsql` API, saving `2.69 MB` in size for the native executable. [[#592](https://github.com/Azure/azure-mcp/pull/592)] + - Enabled `IlcFoldIdenticalMethodBodies` to fold identical method bodies, saving `3.64 MB` in size for the native executable. [[#598](https://github.com/Azure/azure-mcp/pull/598)] +- Added support for using the hyphen/dash ("-") character in command names. [[#531](https://github.com/Azure/azure-mcp/pull/531)] +- Added support for authenticating with the Azure account used to log into VS Code. Authentication now prioritizes the VS Code broker credential when in the context of VS Code. [[#452](https://github.com/Azure/azure-mcp/pull/452)] + +### Breaking Changes + +- Removed SSE (Server-Sent Events) transport support. Now, only stdio transport is supported as SSE is no longer part of the MCP specification. [[#593](https://github.com/Azure/azure-mcp/issues/593)] +- Renamed `azmcp-sql-server-entraadmin-list` to `azmcp-sql-server-entra-admin-list` for better readability. [[#602](https://github.com/Azure/azure-mcp/pull/602)] + +### Bugs Fixed + +- Added a post-install script to ensure platform-specific versions like `@azure/mcp-${platform}-${arch}` can be resolved. Otherwise, fail install to prevent npx caching of `@azure/mcp`. [[#597](https://github.com/Azure/azure-mcp/pull/597)] +- Improved install reliability and error handling when missing platform packages on Ubuntu. [[#394](https://github.com/Azure/azure-mcp/pull/394)] + +### Other Changes +- Updated `engines.node` in `package.json` to require Node.js version `>=22.0.0`. + +#### Dependency Updates + +- Updated the `ModelContextProtocol.AspNetCore` version from `0.3.0-preview.1` to `0.3.0-preview.2`. [[#519](https://github.com/Azure/azure-mcp/pull/519)] + +## 0.3.2 (2025-07-10) + +### Features Added + +- Added support for listing Azure Managed Grafana details via the command: `azmcp-grafana-list`. [[#532](https://github.com/Azure/azure-mcp/pull/532)] +- Added agent best practices for Azure Terraform commands. [[#420](https://github.com/Azure/azure-mcp/pull/420)] + +### Bugs Fixed + +- Fixed issue where trace logs could be collected as telemetry. [[#540](https://github.com/Azure/azure-mcp/pull/540/)] +- Fixed an issue that prevented the Azure MCP from finding the Azure CLI if it was installed on a path other than the default global one. [[#351](https://github.com/Azure/azure-mcp/issues/351)] + +## 0.3.1 (2025-07-08) + +### Features Added + +- Added support for the following SQL operations: + - `azmcp-sql-db-show` - Show details of a SQL Database [[#516](https://github.com/Azure/azure-mcp/pull/516)] + - `azmcp-sql-server-entra-admin-list` - List Microsoft Entra ID administrators for a SQL server [[#529](https://github.com/Azure/azure-mcp/pull/529)] +- Updates Azure MCP tool loading configurations at launch time. [[#513](https://github.com/Azure/azure-mcp/pull/513)] + +### Breaking Changes + +- Deprecated the `--service` flag. Use `--namespace` and `--mode` options to specify the service and mode the server will run in. [[#513](https://github.com/Azure/azure-mcp/pull/513)] + +## 0.3.0 (2025-07-03) + +### Features Added + +- Added support for Azure AI Foundry [[#274](https://github.com/Azure/azure-mcp/pull/274)]. The following tools are now available: + - `azmcp-foundry-models-list` + - `azmcp-foundry-models-deploy` + - `azmcp-foundry-models-deployments-list` +- Added support for telemetry [[#386](https://github.com/Azure/azure-mcp/pull/386)]. Telemetry is enabled by default but can be disabled by setting `AZURE_MCP_COLLECT_TELEMETRY` to `false`. + +### Bugs Fixed + +- Fixed a bug where `CallToolResult` was always successful. [[#511](https://github.com/Azure/azure-mcp/pull/511)] + +## 0.2.6 (2025-07-01) + +### Other Changes + +- Updated the descriptions of the following tools to improve their usage by Agents: [[#492](https://github.com/Azure/azure-mcp/pull/492)] + - `azmcp-datadog-monitoredresources-list` + - `azmcp-kusto-cluster-list` + - `azmcp-kusto-database-list` + - `azmcp-kusto-sample` + - `azmcp-kusto-table-list` + - `azmcp-kusto-table-schema` + +## 0.2.5 (2025-06-26) + +### Bugs Fixed + +- Fixed issue where tool listing incorrectly returned resources instead of text. [#465](https://github.com/Azure/azure-mcp/issues/465) +- Fixed invalid modification to HttpClient in KustoClient. [#433](https://github.com/Azure/azure-mcp/issues/433) + +## 0.2.4 (2025-06-24) + +### Features Added + +- Added new command for resource-centric logs query in Azure Monitor with command path `azmcp-monitor-resource-logs-query` - https://github.com/Azure/azure-mcp/pull/413 +- Added support for starting the server with a subset of services using the `--service` flag - https://github.com/Azure/azure-mcp/pull/424 +- Improved index schema handling in Azure AI Search (index descriptions, facetable fields, etc.) - https://github.com/Azure/azure-mcp/pull/440 +- Added new commands for querying metrics with Azure Monitor with command paths `azmcp-monitor-metrics-query` and `azmcp-monitor-metrics-definitions`. - https://github.com/Azure/azure-mcp/pull/428 + +### Breaking Changes + +- Changed the command for workspace-based logs query in Azure Monitor from `azmcp-monitor-log-query` to `azmcp-monitor-workspace-logs-query` + +### Bugs Fixed + +- Fixed handling of non-retrievable fields in Azure AI Search. [#416](https://github.com/Azure/azure-mcp/issues/416) + +### Other Changes + +- Repository structure changed to organize all of an Azure service's code into a single "area" folder. ([426](https://github.com/Azure/azure-mcp/pull/426)) +- Upgraded Azure.Messaging.ServiceBus to 7.20.1 and Azure.Core to 1.46.2. ([441](https://github.com/Azure/azure-mcp/pull/441/)) +- Updated to ModelContextProtocol 0.3.0-preview1, which brings support for the 06-18-2025 MCP specification. ([431](https://github.com/Azure/azure-mcp/pull/431)) + +## 0.2.3 (2025-06-19) + +### Features Added + +- Adds support to launch MCP server in readonly mode - https://github.com/Azure/azure-mcp/pull/410 + +### Bugs Fixed + +- MCP tools now expose annotations to clients https://github.com/Azure/azure-mcp/pull/388 + +## 0.2.2 (2025-06-17) + +### Features Added + +- Support for Azure ISV Services https://github.com/Azure/azure-mcp/pull/199/ +- Support for Azure RBAC https://github.com/Azure/azure-mcp/pull/266 +- Support for Key Vault Secrets https://github.com/Azure/azure-mcp/pull/173 + + +## 0.2.1 (2025-06-12) + +### Bugs Fixed + +- Fixed the issue where queries containing double quotes failed to execute. https://github.com/Azure/azure-mcp/pull/338 +- Enables dynamic proxy mode within single "azure" tool. https://github.com/Azure/azure-mcp/pull/325 + +## 0.2.0 (2025-06-09) + +### Features Added + +- Support for launching smaller service level MCP servers. https://github.com/Azure/azure-mcp/pull/324 + +### Bugs Fixed + +- Fixed failure starting Docker image. https://github.com/Azure/azure-mcp/pull/301 + +## 0.1.2 (2025-06-03) + +### Bugs Fixed + +- Monitor Query Logs Failing. Fixed with https://github.com/Azure/azure-mcp/pull/280 + +## 0.1.1 (2025-05-30) + +### Bugs Fixed + +- Fixed return value of `tools/list` to use JSON object names. https://github.com/Azure/azure-mcp/pull/275 + +### Other Changes + +- Update .NET SDK version to 9.0.300 https://github.com/Azure/azure-mcp/pull/278 + +## 0.1.0 (2025-05-28) + +### Breaking Changes + +- `azmcp tool list` "args" changes to "options" + +### Other Changes + +- Removed "Arguments" from code base in favor of "Options" to align with System. CommandLine semantics. https://github.com/Azure/azure-mcp/pull/232 + +## 0.0.21 (2025-05-22) + +### Features Added + +- Support for Azure Redis Caches and Clusters https://github.com/Azure/azure-mcp/pull/198 +- Support for Azure Monitor Health Models https://github.com/Azure/azure-mcp/pull/208 + +### Bugs Fixed + +- Updates the usage patterns of Azure Developer CLI (azd) when invoked from MCP. https://github.com/Azure/azure-mcp/pull/203 +- Fixes server binding issue when using SSE transport in Docker by replacing `ListenLocalhost` with `ListenAnyIP`, allowing external access via port mapping. https://github.com/Azure/azure-mcp/pull/233 + +### Other Changes + +- Updated to the latest ModelContextProtocol library. https://github.com/Azure/azure-mcp/pull/220 + +## 0.0.20 (2025-05-17) + +### Bugs Fixed + +- Improve the formatting in the ParseJsonOutput method and refactor it to utilize a ParseError record. https://github.com/Azure/azure-mcp/pull/218 +- Added dummy argument for best practices tool, so the schema is properly generated for Python Open API use cases. https://github.com/Azure/azure-mcp/pull/219 + +## 0.0.19 (2025-05-15) + +### Bugs Fixed + +- Fixes Service Bus host name parameter description. https://github.com/Azure/azure-mcp/pull/209/ + +## 0.0.18 (2025-05-14) + +### Bugs Fixed + +- Include option to exclude managed keys. https://github.com/Azure/azure-mcp/pull/202 + +## 0.0.17 (2025-05-13) + +### Bugs Fixed + +- Added an opt-in timeout for browser-based authentication to handle cases where the process waits indefinitely if the user closes the browser. https://github.com/Azure/azure-mcp/pull/189 + +## 0.0.16 (2025-05-13) + +### Bugs Fixed + +- Fixed being able to pass args containing spaces through an npx call to the cli + +### Other Changes + +- Updated to the latest ModelContextProtocol library. https://github.com/Azure/azure-mcp/pull/161 + +## 0.0.15 (2025-05-09) + +### Features Added + +- Support for getting properties and runtime information for Azure Service Bus queues, topics, and subscriptions. https://github.com/Azure/azure-mcp/pull/150/ +- Support for peeking at Azure Service Bus messages from queues or subscriptions. https://github.com/Azure/azure-mcp/pull/144 +- Adds Best Practices tool that provides guidance to LLMs for effective code generation. https://github.com/Azure/azure-mcp/pull/153 https://github.com/Azure/azure-mcp/pull/156 + +### Other Changes + +- Disabled Parallel testing in the ADO pipeline for Live Tests https://github.com/Azure/azure-mcp/pull/151 + +## 0.0.14 (2025-05-07) + +### Features Added + +- Support for Azure Key Vault keys https://github.com/Azure/azure-mcp/pull/119 +- Support for Azure Data Explorer https://github.com/Azure/azure-mcp/pull/21 + +## 0.0.13 (2025-05-06) + +### Features Added + +- Support for Azure PostgreSQL. https://github.com/Azure/azure-mcp/pull/81 + +## 0.0.12 (2025-05-05) + +### Features Added + +- Azure Search Tools https://github.com/Azure/azure-mcp/pull/83 + +### Other Changes + +- Arguments no longer echoed in response: https://github.com/Azure/azure-mcp/pull/79 +- Editorconfig and gitattributes updated: https://github.com/Azure/azure-mcp/pull/91 + +## 0.0.11 (2025-04-29) + +### Features Added + +### Breaking Changes + +### Bugs Fixed +- Bug fixes to existing MCP commands +- See https://github.com/Azure/azure-mcp/releases/tag/0.0.11 + +### Other Changes + +## 0.0.10 (2025-04-17) + +### Features Added +- Support for Azure Cosmos DB (NoSQL databases). +- Support for Azure Storage. +- Support for Azure Monitor (Log Analytics). +- Support for Azure App Configuration. +- Support for Azure Resource Groups. +- Support for Azure CLI. +- Support for Azure Developer CLI (azd). + +### Breaking Changes + +### Bugs Fixed +- See https://github.com/Azure/azure-mcp/releases/tag/0.0.10 -### Added -- Initial Release +### Other Changes +- See Blog post for details https://devblogs.microsoft.com/azure-sdk/introducing-the-azure-mcp-server/ diff --git a/servers/Azure.Mcp.Server/CHANGELOG.md b/servers/Azure.Mcp.Server/CHANGELOG.md index e8555e5862..abcdc503ae 100644 --- a/servers/Azure.Mcp.Server/CHANGELOG.md +++ b/servers/Azure.Mcp.Server/CHANGELOG.md @@ -20,12 +20,14 @@ The Azure MCP Server updates automatically by default whenever a new release com ### Bugs Fixed +- Fixed MCP server hanging on invalid transport arguments. Server now exits gracefully with clear error messages instead of hanging indefinitely. [[#311](https://github.com/microsoft/mcp/issues/311)] [[#511](https://github.com/microsoft/mcp/pull/511)] + ### Other Changes ## 0.8.0 (2025-09-18) ### Features Added - + - Added the `--insecure-disable-elicitation` server startup switch. When enabled, the server will bypass user confirmation (elicitation) for tools marked as handling secrets and execute them immediately. This is **INSECURE** and meant only for controlled automation scenarios (e.g., CI or disposable test environments) because it removes a safety barrier that helps prevent accidental disclosure of sensitive data. [[#486](https://github.com/microsoft/mcp/pull/486)] - Enhanced Azure authentication with targeted credential selection via the `AZURE_TOKEN_CREDENTIALS` environment variable: [[#56](https://github.com/microsoft/mcp/pull/56)] - `"dev"`: Development credentials (Visual Studio → Visual Studio Code → Azure CLI → Azure PowerShell → Azure Developer CLI) @@ -50,6 +52,7 @@ The Azure MCP Server updates automatically by default whenever a new release com - Removed support for the `AZURE_MCP_INCLUDE_PRODUCTION_CREDENTIALS` environment variable. Use `AZURE_TOKEN_CREDENTIALS` instead for more flexible credential selection. For migration details, see [Controlling Authentication Methods with AZURE_TOKEN_CREDENTIALS](https://github.com/microsoft/mcp/blob/main/servers/Azure.Mcp.Server/TROUBLESHOOTING.md#controlling-authentication-methods-with-azure_token_credentials). [[#56](https://github.com/microsoft/mcp/pull/56)] - Merged `azmcp_appconfig_kv_lock` and `azmcp_appconfig_kv_unlock` into `azmcp_appconfig_kv_lock_set` which can handle locking or unlocking a key-value based on the `--lock` parameter. [[#485](https://github.com/microsoft/mcp/pull/485)] + ### Other Changes - Update `azmcp_foundry_models_deploy` to use "GenericResource" for deploying models to Azure AI Services. [[#456](https://github.com/microsoft/mcp/pull/456)] diff --git a/tools/Azure.Mcp.Tools.AzureBestPractices/src/Commands/BestPracticesCommand.cs b/tools/Azure.Mcp.Tools.AzureBestPractices/src/Commands/BestPracticesCommand.cs index 823dbea862..6d5acc9c6e 100644 --- a/tools/Azure.Mcp.Tools.AzureBestPractices/src/Commands/BestPracticesCommand.cs +++ b/tools/Azure.Mcp.Tools.AzureBestPractices/src/Commands/BestPracticesCommand.cs @@ -12,7 +12,7 @@ namespace Azure.Mcp.Tools.AzureBestPractices.Commands; -public sealed class BestPracticesCommand(ILogger logger) : BaseCommand +public sealed class BestPracticesCommand(ILogger logger) : BaseCommand { private const string CommandTitle = "Get Azure Best Practices"; private readonly ILogger _logger = logger; @@ -47,7 +47,7 @@ protected override void RegisterOptions(Command command) command.Options.Add(BestPracticesOptionDefinitions.Action); } - private BestPracticesOptions BindOptions(ParseResult parseResult) + protected override BestPracticesOptions BindOptions(ParseResult parseResult) { return new BestPracticesOptions { diff --git a/tools/Azure.Mcp.Tools.AzureTerraformBestPractices/src/Commands/AzureTerraformBestPracticesGetCommand.cs b/tools/Azure.Mcp.Tools.AzureTerraformBestPractices/src/Commands/AzureTerraformBestPracticesGetCommand.cs index a26aa8c71b..de38948367 100644 --- a/tools/Azure.Mcp.Tools.AzureTerraformBestPractices/src/Commands/AzureTerraformBestPracticesGetCommand.cs +++ b/tools/Azure.Mcp.Tools.AzureTerraformBestPractices/src/Commands/AzureTerraformBestPracticesGetCommand.cs @@ -8,7 +8,7 @@ namespace Azure.Mcp.Tools.AzureTerraformBestPractices.Commands; -public sealed class AzureTerraformBestPracticesGetCommand(ILogger logger) : BaseCommand() +public sealed class AzureTerraformBestPracticesGetCommand(ILogger logger) : BaseCommand { private const string CommandTitle = "Get Terraform Best Practices for Azure"; private readonly ILogger _logger = logger; diff --git a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Architecture/DiagramGenerateCommand.cs b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Architecture/DiagramGenerateCommand.cs index e6ed777194..272cbe8085 100644 --- a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Architecture/DiagramGenerateCommand.cs +++ b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Architecture/DiagramGenerateCommand.cs @@ -12,7 +12,7 @@ namespace Azure.Mcp.Tools.Deploy.Commands.Architecture; -public sealed class DiagramGenerateCommand(ILogger logger) : BaseCommand() +public sealed class DiagramGenerateCommand(ILogger logger) : BaseCommand { private const string CommandTitle = "Generate Architecture Diagram"; private readonly ILogger _logger = logger; @@ -43,7 +43,7 @@ protected override void RegisterOptions(Command command) command.Options.Add(DeployOptionDefinitions.RawMcpToolInput.RawMcpToolInputOption); } - private DiagramGenerateOptions BindOptions(ParseResult parseResult) + protected override DiagramGenerateOptions BindOptions(ParseResult parseResult) { var options = new DiagramGenerateOptions(); options.RawMcpToolInput = parseResult.GetValueOrDefault(DeployOptionDefinitions.RawMcpToolInput.RawMcpToolInputOption.Name); diff --git a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Infrastructure/RulesGetCommand.cs b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Infrastructure/RulesGetCommand.cs index e64fed5b79..fa2f742093 100644 --- a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Infrastructure/RulesGetCommand.cs +++ b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Infrastructure/RulesGetCommand.cs @@ -12,7 +12,7 @@ namespace Azure.Mcp.Tools.Deploy.Commands.Infrastructure; public sealed class RulesGetCommand(ILogger logger) - : BaseCommand() + : BaseCommand { private const string CommandTitle = "Get Iac(Infrastructure as Code) Rules"; private readonly ILogger _logger = logger; @@ -42,7 +42,7 @@ protected override void RegisterOptions(Command command) command.Options.Add(DeployOptionDefinitions.IaCRules.ResourceTypes); } - private RulesGetOptions BindOptions(ParseResult parseResult) + protected override RulesGetOptions BindOptions(ParseResult parseResult) { var options = new RulesGetOptions(); options.DeploymentTool = parseResult.GetValueOrDefault(DeployOptionDefinitions.IaCRules.DeploymentTool.Name) ?? string.Empty; diff --git a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Plan/GetCommand.cs b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Plan/GetCommand.cs index cc9fe7482b..0608d3f39f 100644 --- a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Plan/GetCommand.cs +++ b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Plan/GetCommand.cs @@ -14,7 +14,7 @@ namespace Azure.Mcp.Tools.Deploy.Commands.Plan; public sealed class GetCommand(ILogger logger) - : BaseCommand() + : BaseCommand { private const string CommandTitle = "Generate Azure Deployment Plan"; private readonly ILogger _logger = logger; @@ -47,7 +47,7 @@ protected override void RegisterOptions(Command command) command.Options.Add(DeployOptionDefinitions.PlanGet.AzdIacOptions); } - private GetOptions BindOptions(ParseResult parseResult) + protected override GetOptions BindOptions(ParseResult parseResult) { return new GetOptions { diff --git a/tools/Azure.Mcp.Tools.KeyVault/src/Services/KeyVaultService.cs b/tools/Azure.Mcp.Tools.KeyVault/src/Services/KeyVaultService.cs index a9a5cd86b8..dfdc91fb2f 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/src/Services/KeyVaultService.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/src/Services/KeyVaultService.cs @@ -256,9 +256,9 @@ public async Task ImportCertificate( else { // Try base64, fallback to file path if exists - if (System.IO.File.Exists(certificateData)) + if (File.Exists(certificateData)) { - bytes = await System.IO.File.ReadAllBytesAsync(certificateData); + bytes = await File.ReadAllBytesAsync(certificateData); } else { diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs index 12eabc24dd..38b21a239f 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs @@ -106,7 +106,7 @@ public async Task CreateDatabaseAsync( { // Use ARM client directly for create operations var armClient = await CreateArmClientAsync(null, retryPolicy); - var subscriptionResource = armClient.GetSubscriptionResource(Azure.ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); + var subscriptionResource = armClient.GetSubscriptionResource(ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); var resourceGroupResource = await subscriptionResource.GetResourceGroupAsync(resourceGroup); var sqlServerResource = await resourceGroupResource.Value.GetSqlServers().GetAsync(serverName); @@ -157,7 +157,7 @@ public async Task CreateDatabaseAsync( } var operation = await sqlServerResource.Value.GetSqlDatabases().CreateOrUpdateAsync( - Azure.WaitUntil.Completed, + WaitUntil.Completed, databaseName, databaseData, cancellationToken); @@ -219,7 +219,7 @@ public async Task UpdateDatabaseAsync( try { var armClient = await CreateArmClientAsync(null, retryPolicy); - var subscriptionResource = armClient.GetSubscriptionResource(Azure.ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); + var subscriptionResource = armClient.GetSubscriptionResource(ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); var resourceGroupResource = await subscriptionResource.GetResourceGroupAsync(resourceGroup); var sqlServerResource = await resourceGroupResource.Value.GetSqlServers().GetAsync(serverName); @@ -267,7 +267,7 @@ public async Task UpdateDatabaseAsync( } var operation = await sqlServerResource.Value.GetSqlDatabases().CreateOrUpdateAsync( - Azure.WaitUntil.Completed, + WaitUntil.Completed, databaseName, databaseData, cancellationToken); @@ -468,7 +468,7 @@ public async Task CreateFirewallRuleAsync( { // Use ARM client directly for create operations var armClient = await CreateArmClientAsync(null, retryPolicy); - var subscriptionResource = armClient.GetSubscriptionResource(Azure.ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); + var subscriptionResource = armClient.GetSubscriptionResource(ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); var resourceGroupResource = await subscriptionResource.GetResourceGroupAsync(resourceGroup); var sqlServerResource = await resourceGroupResource.Value.GetSqlServers().GetAsync(serverName); @@ -479,7 +479,7 @@ public async Task CreateFirewallRuleAsync( }; var operation = await sqlServerResource.Value.GetSqlFirewallRules().CreateOrUpdateAsync( - Azure.WaitUntil.Completed, + WaitUntil.Completed, firewallRuleName, firewallRuleData, cancellationToken); @@ -528,13 +528,13 @@ public async Task DeleteFirewallRuleAsync( { // Use ARM client directly for delete operations var armClient = await CreateArmClientAsync(null, retryPolicy); - var subscriptionResource = armClient.GetSubscriptionResource(Azure.ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); + var subscriptionResource = armClient.GetSubscriptionResource(ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); var resourceGroupResource = await subscriptionResource.GetResourceGroupAsync(resourceGroup); var sqlServerResource = await resourceGroupResource.Value.GetSqlServers().GetAsync(serverName); var firewallRuleResource = await sqlServerResource.Value.GetSqlFirewallRules().GetAsync(firewallRuleName); - await firewallRuleResource.Value.DeleteAsync(Azure.WaitUntil.Completed, cancellationToken); + await firewallRuleResource.Value.DeleteAsync(WaitUntil.Completed, cancellationToken); _logger.LogInformation( "Successfully deleted SQL server firewall rule. Server: {Server}, ResourceGroup: {ResourceGroup}, Rule: {Rule}", @@ -593,7 +593,7 @@ public async Task CreateServerAsync( { // Use ARM client directly for create operations var armClient = await CreateArmClientAsync(null, retryPolicy); - var subscriptionResource = armClient.GetSubscriptionResource(Azure.ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); + var subscriptionResource = armClient.GetSubscriptionResource(ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); var resourceGroupResource = await subscriptionResource.GetResourceGroupAsync(resourceGroup); var serverData = new SqlServerData(location) @@ -613,7 +613,7 @@ public async Task CreateServerAsync( } var operation = await resourceGroupResource.Value.GetSqlServers().CreateOrUpdateAsync( - Azure.WaitUntil.Completed, + WaitUntil.Completed, serverName, serverData, cancellationToken); @@ -667,7 +667,7 @@ public async Task GetServerAsync( { // Use ARM client directly for get operations var armClient = await CreateArmClientAsync(null, retryPolicy); - var subscriptionResource = armClient.GetSubscriptionResource(Azure.ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); + var subscriptionResource = armClient.GetSubscriptionResource(ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); var resourceGroupResource = await subscriptionResource.GetResourceGroupAsync(resourceGroup); var serverResource = await resourceGroupResource.Value.GetSqlServers().GetAsync(serverName); @@ -767,13 +767,13 @@ public async Task DeleteServerAsync( { // Use ARM client directly for delete operations var armClient = await CreateArmClientAsync(null, retryPolicy); - var subscriptionResource = armClient.GetSubscriptionResource(Azure.ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); + var subscriptionResource = armClient.GetSubscriptionResource(ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); var resourceGroupResource = await subscriptionResource.GetResourceGroupAsync(resourceGroup); var serverResource = await resourceGroupResource.Value.GetSqlServers().GetAsync(serverName); var operation = await serverResource.Value.DeleteAsync( - Azure.WaitUntil.Completed, + WaitUntil.Completed, cancellationToken); return true; @@ -819,13 +819,13 @@ public async Task DeleteDatabaseAsync( { // Use ARM client directly for delete operations var armClient = await CreateArmClientAsync(null, retryPolicy); - var subscriptionResource = armClient.GetSubscriptionResource(Azure.ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); + var subscriptionResource = armClient.GetSubscriptionResource(ResourceManager.Resources.SubscriptionResource.CreateResourceIdentifier(subscription)); var resourceGroupResource = await subscriptionResource.GetResourceGroupAsync(resourceGroup); var sqlServerResource = await resourceGroupResource.Value.GetSqlServers().GetAsync(serverName); var databaseResource = await sqlServerResource.Value.GetSqlDatabases().GetAsync(databaseName); - await databaseResource.Value.DeleteAsync(Azure.WaitUntil.Completed, cancellationToken); + await databaseResource.Value.DeleteAsync(WaitUntil.Completed, cancellationToken); _logger.LogInformation( "Successfully deleted SQL database. Server: {Server}, Database: {Database}, ResourceGroup: {ResourceGroup}", @@ -882,7 +882,7 @@ private static SqlDatabase ConvertToSqlDatabaseModel(SqlDatabaseResource databas private static SqlDatabase ConvertToSqlDatabaseModel(JsonElement item) { - Models.SqlDatabaseData? sqlDatabase = Azure.Mcp.Tools.Sql.Services.Models.SqlDatabaseData.FromJson(item); + Models.SqlDatabaseData? sqlDatabase = Models.SqlDatabaseData.FromJson(item); if (sqlDatabase == null) throw new InvalidOperationException("Failed to parse SQL database data"); @@ -985,7 +985,7 @@ private static SqlElasticPool ConvertToSqlElasticPoolModel(JsonElement item) private static SqlServerFirewallRule ConvertToSqlFirewallRuleModel(JsonElement item) { - Models.SqlFirewallRuleData? firewallRule = Azure.Mcp.Tools.Sql.Services.Models.SqlFirewallRuleData.FromJson(item); + Models.SqlFirewallRuleData? firewallRule = Models.SqlFirewallRuleData.FromJson(item); if (firewallRule == null) throw new InvalidOperationException("Failed to parse SQL firewall rule data"); diff --git a/tools/Azure.Mcp.Tools.Workbooks/src/Services/WorkbooksService.cs b/tools/Azure.Mcp.Tools.Workbooks/src/Services/WorkbooksService.cs index 0b550cf162..2a0e0411e9 100644 --- a/tools/Azure.Mcp.Tools.Workbooks/src/Services/WorkbooksService.cs +++ b/tools/Azure.Mcp.Tools.Workbooks/src/Services/WorkbooksService.cs @@ -235,7 +235,7 @@ public async Task> ListWorkbooks(string subscription, string // Create the workbook var workbookCollection = resourceGroupResource.Value.GetApplicationInsightsWorkbooks(); - var createOperation = await workbookCollection.CreateOrUpdateAsync(Azure.WaitUntil.Completed, workbookName, workbookData); + var createOperation = await workbookCollection.CreateOrUpdateAsync(WaitUntil.Completed, workbookName, workbookData); var createdWorkbook = createOperation.Value; _logger.LogInformation("Successfully created workbook with name: {WorkbookName} in resource group: {ResourceGroup}", workbookName, resourceGroupName); @@ -275,7 +275,7 @@ public async Task DeleteWorkbook(string workbookId, RetryPolicyOptions? re var workbookResource = armClient.GetApplicationInsightsWorkbookResource(workbookResourceId) ?? throw new Exception($"Workbook with ID '{workbookId}' not found"); // Delete the workbook - var response = await workbookResource.DeleteAsync(Azure.WaitUntil.Completed); + var response = await workbookResource.DeleteAsync(WaitUntil.Completed); _logger.LogInformation("Successfully deleted workbook with ID: {WorkbookId}", workbookId); return true; From c0dd53034cd6f03aa5605d192378b0db84f2c199 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:06:46 -0700 Subject: [PATCH 03/10] Formatting --- core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs | 2 +- .../Areas/Server/ServiceStartCommandTests.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs b/core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs index 7601d45a59..7343e03ee4 100644 --- a/core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs +++ b/core/Azure.Mcp.Core/src/Commands/EmptyOptions.cs @@ -8,4 +8,4 @@ namespace Azure.Mcp.Core.Commands; /// public sealed class EmptyOptions { -} \ No newline at end of file +} diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs index 1383a88f34..9b3d42fa24 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs @@ -523,7 +523,7 @@ private static ParseResult CreateParseResultWithMinimalOptions() private ServiceStartOptions GetBoundOptions(ParseResult parseResult) { // Use reflection to access the protected BindOptions method - var method = typeof(ServiceStartCommand).GetMethod("BindOptions", + var method = typeof(ServiceStartCommand).GetMethod("BindOptions", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); return (ServiceStartOptions)method!.Invoke(_command, new object[] { parseResult })!; } @@ -531,7 +531,7 @@ private ServiceStartOptions GetBoundOptions(ParseResult parseResult) private string GetErrorMessage(Exception exception) { // Use reflection to access the protected GetErrorMessage method - var method = typeof(ServiceStartCommand).GetMethod("GetErrorMessage", + var method = typeof(ServiceStartCommand).GetMethod("GetErrorMessage", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); return (string)method!.Invoke(_command, new object[] { exception })!; } @@ -539,7 +539,7 @@ private string GetErrorMessage(Exception exception) private int GetStatusCode(Exception exception) { // Use reflection to access the protected GetStatusCode method - var method = typeof(ServiceStartCommand).GetMethod("GetStatusCode", + var method = typeof(ServiceStartCommand).GetMethod("GetStatusCode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); return (int)method!.Invoke(_command, new object[] { exception })!; } From 4c9e2ef5e58382c8028ef583fac4ca2149785155 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:23:44 -0700 Subject: [PATCH 04/10] Refactor validation method to remove nullable parameter and update tests accordingly --- .../src/Areas/Server/Commands/ServiceStartCommand.cs | 2 +- core/Azure.Mcp.Core/src/Commands/BaseCommand.cs | 6 +++--- .../Areas/Server/ServiceStartCommandTests.cs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs index 9d42c419e2..62d9c16098 100644 --- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs +++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs @@ -90,7 +90,7 @@ protected override ServiceStartOptions BindOptions(ParseResult parseResult) /// The command result to validate. /// Optional response object to set error details. /// A ValidationResult indicating whether the validation passed. - public override ValidationResult Validate(CommandResult commandResult, CommandResponse? commandResponse = null) + public override ValidationResult Validate(CommandResult commandResult, CommandResponse? commandResponse) { // First run the base validation for required options and parser errors var baseResult = base.Validate(commandResult, commandResponse); diff --git a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs index 1c9041423d..b4ba79f6e2 100644 --- a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs +++ b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs @@ -9,7 +9,7 @@ namespace Azure.Mcp.Core.Commands; -public abstract class BaseCommand : IBaseCommand where TOptions : class, new() +public abstract class BaseCommand : IBaseCommand where TOptions : class { private const string MissingRequiredOptionsPrefix = "Missing Required options: "; private const int ValidationErrorStatusCode = 400; @@ -42,8 +42,8 @@ protected virtual void RegisterOptions(Command command) /// An options object containing the bound options. protected virtual TOptions BindOptions(ParseResult parseResult) { - // If no specific binding is implemented, create a default instance - return Activator.CreateInstance(); + // This method must be overridden by derived classes that need option binding + throw new NotImplementedException($"Command '{GetType().Name}' must override BindOptions to provide option binding."); } public abstract Task ExecuteAsync(CommandContext context, ParseResult parseResult); diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs index 9b3d42fa24..4911b55e7c 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs @@ -196,7 +196,7 @@ public void Validate_WithValidOptions_ReturnsValidResult() var commandResult = parseResult.CommandResult; // Act - var result = _command.Validate(commandResult); + var result = _command.Validate(commandResult, null); // Assert Assert.True(result.IsValid); @@ -211,7 +211,7 @@ public void Validate_WithInvalidTransport_ReturnsInvalidResult() var commandResult = parseResult.CommandResult; // Act - var result = _command.Validate(commandResult); + var result = _command.Validate(commandResult, null); // Assert Assert.False(result.IsValid); @@ -226,7 +226,7 @@ public void Validate_WithInvalidMode_ReturnsInvalidResult() var commandResult = parseResult.CommandResult; // Act - var result = _command.Validate(commandResult); + var result = _command.Validate(commandResult, null); // Assert Assert.False(result.IsValid); From 1fe1f3ed3995e083ed1bcef08c563b62c45408f9 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:28:35 -0700 Subject: [PATCH 05/10] Make BindOptions method abstract to enforce implementation in derived classes --- core/Azure.Mcp.Core/src/Commands/BaseCommand.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs index b4ba79f6e2..bea1fac4fe 100644 --- a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs +++ b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs @@ -40,11 +40,7 @@ protected virtual void RegisterOptions(Command command) /// /// The parsed command line arguments. /// An options object containing the bound options. - protected virtual TOptions BindOptions(ParseResult parseResult) - { - // This method must be overridden by derived classes that need option binding - throw new NotImplementedException($"Command '{GetType().Name}' must override BindOptions to provide option binding."); - } + protected abstract TOptions BindOptions(ParseResult parseResult); public abstract Task ExecuteAsync(CommandContext context, ParseResult parseResult); From 9f1aceb97561526e8a7074175739fb160465b4fa Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Fri, 19 Sep 2025 13:21:06 -0700 Subject: [PATCH 06/10] Refactor validation logic in ServiceStartCommand for improved readability and early returns; add BindOptions method in ToolsListCommand; enhance BaseCommand with option binding and error handling improvements. --- .../Server/Commands/ServiceStartCommand.cs | 146 ++++++++---------- .../Areas/Tools/Commands/ToolsListCommand.cs | 2 + .../src/Commands/BaseCommand.cs | 26 +++- 3 files changed, 90 insertions(+), 84 deletions(-) diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs index 62d9c16098..da8c665a29 100644 --- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs +++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs @@ -102,46 +102,13 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe // Get option values directly from commandResult var mode = commandResult.GetValueOrDefault(ServiceOptionDefinitions.Mode); var transport = commandResult.GetValueOrDefault(ServiceOptionDefinitions.Transport); + var enableInsecureTransports = commandResult.GetValueOrDefault(ServiceOptionDefinitions.EnableInsecureTransports); - if (!IsValidMode(mode)) - { - var result = new ValidationResult - { - IsValid = false, - ErrorMessage = $"Invalid mode '{mode}'. Valid modes are: {ModeTypes.SingleToolProxy}, {ModeTypes.NamespaceProxy}, {ModeTypes.All}." - }; - - if (commandResponse != null) - { - commandResponse.Status = 400; - commandResponse.Message = result.ErrorMessage; - } - - return result; - } - - try - { - ValidateTransport(transport); - } - catch (ArgumentException ex) - { - var result = new ValidationResult - { - IsValid = false, - ErrorMessage = ex.Message - }; - - if (commandResponse != null) - { - commandResponse.Status = 400; - commandResponse.Message = result.ErrorMessage; - } - - return result; - } - - return new ValidationResult { IsValid = true }; + // Validate and return early on any failures + return ValidateMode(mode, commandResponse) ?? + ValidateTransport(transport, commandResponse) ?? + ValidateInsecureTransportsConfiguration(enableInsecureTransports, commandResponse) ?? + new ValidationResult { IsValid = true }; } /// @@ -161,15 +128,6 @@ public override async Task ExecuteAsync(CommandContext context, try { - if (options.EnableInsecureTransports) - { - var includeProdCreds = EnvironmentHelpers.GetEnvironmentVariableAsBool("AZURE_MCP_INCLUDE_PRODUCTION_CREDENTIALS"); - if (!includeProdCreds) - { - throw new InvalidOperationException("Using --enable-insecure-transport requires the host to have either Managed Identity or Workload Identity enabled. Please refer to the troubleshooting guidelines here at https://aka.ms/azmcp/troubleshooting."); - } - } - using var host = CreateHost(options); await host.StartAsync(CancellationToken.None); await host.WaitForShutdownAsync(CancellationToken.None); @@ -187,54 +145,80 @@ public override async Task ExecuteAsync(CommandContext context, /// Validates if the provided mode is a valid mode type. /// /// The mode to validate. - /// True if the mode is valid, otherwise false. - private static bool IsValidMode(string? mode) + /// Optional command response to update on failure. + /// ValidationResult with error details if invalid, null if valid. + private static ValidationResult? ValidateMode(string? mode, CommandResponse? commandResponse) { - return mode == ModeTypes.SingleToolProxy || - mode == ModeTypes.NamespaceProxy || - mode == ModeTypes.All; + if (mode == ModeTypes.SingleToolProxy || + mode == ModeTypes.NamespaceProxy || + mode == ModeTypes.All) + { + return null; // Success + } + + var result = new ValidationResult + { + IsValid = false, + ErrorMessage = $"Invalid mode '{mode}'. Valid modes are: {ModeTypes.SingleToolProxy}, {ModeTypes.NamespaceProxy}, {ModeTypes.All}." + }; + + SetValidationError(commandResponse, result.ErrorMessage!, 400); + return result; } /// - /// Validates the transport parameter and throws an exception if invalid. + /// Validates if the provided transport is valid. /// /// The transport to validate. - /// Thrown when the transport is not null and invalid. - private static void ValidateTransport(string? transport) + /// Optional command response to update on failure. + /// ValidationResult with error details if invalid, null if valid. + private static ValidationResult? ValidateTransport(string? transport, CommandResponse? commandResponse) { - if (transport is not null && transport != TransportTypes.StdIo) + if (transport is null || transport == TransportTypes.StdIo) { - throw new ArgumentException($"Invalid transport '{transport}'. Valid transports are: {TransportTypes.StdIo}."); + return null; // Success } + + var result = new ValidationResult + { + IsValid = false, + ErrorMessage = $"Invalid transport '{transport}'. Valid transports are: {TransportTypes.StdIo}." + }; + + SetValidationError(commandResponse, result.ErrorMessage!, 400); + return result; } /// - /// Provides custom error messages for service start specific exceptions. + /// Validates if the insecure transport configuration is valid. /// - /// The exception to get an error message for. - /// A user-friendly error message. - protected override string GetErrorMessage(Exception ex) => ex switch + /// Whether insecure transports are enabled. + /// Optional command response to update on failure. + /// ValidationResult with error details if invalid, null if valid. + private static ValidationResult? ValidateInsecureTransportsConfiguration(bool enableInsecureTransports, CommandResponse? commandResponse) { - ArgumentException argEx when argEx.Message.Contains("transport") => - $"Invalid transport option specified. {argEx.Message} Use --transport stdio for standard input/output.", - ArgumentException argEx when argEx.Message.Contains("mode") => - $"Invalid mode option specified. {argEx.Message} Use --mode single, namespace, or all.", - InvalidOperationException opEx when opEx.Message.Contains("enable-insecure-transport") => - "Insecure transport configuration error. Ensure your environment has proper authentication configured (Managed Identity or Workload Identity).", - _ => ex.Message - }; + // If insecure transports are not enabled, configuration is valid + if (!enableInsecureTransports) + { + return null; // Success + } - /// - /// Provides appropriate HTTP status codes for service start specific exceptions. - /// - /// The exception to get a status code for. - /// An appropriate HTTP status code. - protected override int GetStatusCode(Exception ex) => ex switch - { - ArgumentException => 400, // Bad Request for invalid arguments - InvalidOperationException => 422, // Unprocessable Entity for configuration errors - _ => 500 - }; + // If insecure transports are enabled, check if proper credentials are configured + var hasCredentials = EnvironmentHelpers.GetEnvironmentVariableAsBool("AZURE_MCP_INCLUDE_PRODUCTION_CREDENTIALS"); + if (hasCredentials) + { + return null; // Success + } + + var result = new ValidationResult + { + IsValid = false, + ErrorMessage = "Using --enable-insecure-transport requires the host to have either Managed Identity or Workload Identity enabled. Please refer to the troubleshooting guidelines here at https://aka.ms/azmcp/troubleshooting." + }; + + SetValidationError(commandResponse, result.ErrorMessage!, 422); + return result; + } /// /// Creates the host for the MCP server with the specified options. diff --git a/core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs b/core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs index 9890f03276..a1c8eb8612 100644 --- a/core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs +++ b/core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs @@ -33,6 +33,8 @@ arguments. Use this to explore the CLI's functionality or to build interactive c Secret = false }; + protected override EmptyOptions BindOptions(ParseResult parseResult) => new(); + public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult) { try diff --git a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs index bea1fac4fe..d8a7df8bc5 100644 --- a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs +++ b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs @@ -9,7 +9,7 @@ namespace Azure.Mcp.Core.Commands; -public abstract class BaseCommand : IBaseCommand where TOptions : class +public abstract class BaseCommand : IBaseCommand where TOptions : class, new() { private const string MissingRequiredOptionsPrefix = "Missing Required options: "; private const int ValidationErrorStatusCode = 400; @@ -36,7 +36,7 @@ protected virtual void RegisterOptions(Command command) /// /// Binds the parsed command line arguments to a strongly-typed options object. - /// Override this method in derived classes to provide custom option binding. + /// Implement this method in derived classes to provide option binding logic. /// /// The parsed command line arguments. /// An options object containing the bound options. @@ -83,7 +83,12 @@ protected virtual void HandleException(CommandContext context, Exception ex) protected virtual string GetErrorMessage(Exception ex) => ex.Message; - protected virtual int GetStatusCode(Exception ex) => 500; + protected virtual int GetStatusCode(Exception ex) => ex switch + { + ArgumentException => 400, // Bad Request for invalid arguments + InvalidOperationException => 422, // Unprocessable Entity for configuration errors + _ => 500 // Internal Server Error for unexpected errors + }; public virtual ValidationResult Validate(CommandResult commandResult, CommandResponse? commandResponse = null) { @@ -126,6 +131,21 @@ static void SetValidationError(CommandResponse? response, string errorMessage) } } } + + /// + /// Sets validation error details on the command response with a custom status code. + /// + /// The command response to update. + /// The error message. + /// The HTTP status code (defaults to ValidationErrorStatusCode). + protected static void SetValidationError(CommandResponse? response, string errorMessage, int statusCode) + { + if (response != null) + { + response.Status = statusCode; + response.Message = errorMessage; + } + } } internal record ExceptionResult( From 43112046e555dc09231c8e48f2aaac28c9518efa Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Fri, 19 Sep 2025 18:58:47 -0700 Subject: [PATCH 07/10] Refactor HTTP status code assertions in unit tests to use HttpStatusCode enum - Updated assertions in SessionHostListCommandTests to replace integer status codes with HttpStatusCode enum values for better readability and maintainability. - Made similar updates across various unit test files including SessionHostUserSessionListCommandTests, CreateWorkbooksCommandTests, DeleteWorkbooksCommandTests, ListWorkbooksCommandTests, ShowWorkbooksCommandTests, UpdateWorkbooksCommandTests, and Public API command tests. - Ensured consistency in error handling by using HttpStatusCode for all relevant assertions. --- .../Server/Commands/ServiceStartCommand.cs | 31 ++++++---- .../ToolLoading/CommandFactoryToolLoader.cs | 3 +- .../src/Commands/BaseCommand.cs | 14 ++--- .../src/Commands/CommandFactory.cs | 10 ++-- .../src/Commands/GlobalCommand.cs | 11 ++-- .../Exceptions/CommandValidationException.cs | 8 ++- .../src/Models/Command/CommandContext.cs | 3 +- .../src/Models/Command/CommandResponse.cs | 21 ++++++- .../Group/UnitTests/GroupListCommandTests.cs | 9 +-- .../CommandGroupDiscoveryStrategyTests.cs | 2 +- .../Discovery/RegistryServerProviderTests.cs | 6 +- .../Commands/Runtime/McpRuntimeTests.cs | 4 +- .../ServiceCollectionExtensionsTests.cs | 4 +- .../CommandFactoryToolLoaderTests.cs | 17 +++--- .../Areas/Server/ServiceStartCommandTests.cs | 25 +++++---- .../SubscriptionListCommandTests.cs | 11 ++-- .../Tools/UnitTests/ToolsListCommandTests.cs | 9 ++- docs/new-command.md | 56 ++++++++++--------- .../Services/EmbeddingService.cs | 2 +- servers/Azure.Mcp.Server/src/Program.cs | 3 +- servers/Fabric.Mcp.Server/src/Program.cs | 3 +- servers/Template.Mcp.Server/src/Program.cs | 3 +- .../Registry/RegistryListCommandTests.cs | 11 ++-- .../RegistryRepositoryListCommandTests.cs | 7 ++- .../src/Commands/Cluster/ClusterGetCommand.cs | 5 +- .../Commands/Cluster/ClusterListCommand.cs | 5 +- .../Commands/Nodepool/NodepoolGetCommand.cs | 5 +- .../Commands/Nodepool/NodepoolListCommand.cs | 5 +- .../Cluster/ClusterGetCommandTests.cs | 15 ++--- .../Cluster/ClusterListCommandTests.cs | 9 +-- .../Nodepool/NodepoolGetCommandTests.cs | 9 +-- .../Nodepool/NodepoolListCommandTests.cs | 9 +-- .../Account/AccountListCommandTests.cs | 11 ++-- .../KeyValue/KeyValueDeleteCommandTests.cs | 9 +-- .../KeyValue/KeyValueListCommandTests.cs | 11 ++-- .../KeyValue/KeyValueSetCommandTests.cs | 11 ++-- .../KeyValue/KeyValueShowCommandTests.cs | 9 +-- .../Lock/KeyValueLockSetCommandTests.cs | 13 +++-- .../Resource/ResourceDiagnoseCommandTests.cs | 23 ++++---- .../RoleAssignmentListCommandTests.cs | 3 +- .../src/Commands/BestPracticesCommand.cs | 7 ++- .../BestPracticesCommandTests.cs | 25 +++++---- .../MonitoredResourcesListCommandTests.cs | 3 +- .../FileSystem/FileSystemSubnetSizeCommand.cs | 3 +- .../FileSystem/FileSystemListCommandTests.cs | 9 +-- .../FileSystemSubnetSizeCommandTests.cs | 7 ++- .../FileSystem/Sku/SkuGetCommandTests.cs | 3 +- .../AzureTerraformBestPracticesGetCommand.cs | 5 +- .../src/Commands/Design/DesignCommand.cs | 3 +- .../Design/DesignCommandTests.cs | 21 +++---- .../src/Commands/BaseCosmosCommand.cs | 5 +- .../AccountListCommandTests.cs | 7 ++- .../ContainerListCommandTests.cs | 11 ++-- .../DatabaseListCommandTests.cs | 11 ++-- .../ItemQueryCommandTests.cs | 7 ++- .../Architecture/DiagramGenerateCommand.cs | 3 +- .../Commands/Pipeline/GuidanceGetCommand.cs | 3 +- .../src/Commands/Plan/GetCommand.cs | 3 +- .../Commands/App/LogsGetCommandTests.cs | 13 +++-- .../DiagramGenerateCommandTests.cs | 7 ++- .../Infrastructure/RulesGetCommandTests.cs | 15 ++--- .../Pipeline/GuidanceGetCommandTests.cs | 15 ++--- .../Commands/Plan/GetCommandTests.cs | 9 +-- .../Subscription/SubscriptionListCommand.cs | 9 +-- .../SubscriptionListCommandTests.cs | 7 ++- .../Topic/TopicListCommandTests.cs | 7 ++- .../src/Commands/AzCommand.cs | 3 +- .../src/Commands/AzdCommand.cs | 7 ++- .../src/Commands/AzqrCommand.cs | 7 ++- .../AzqrCommandTests.cs | 5 +- .../DeploymentsListCommandTests.cs | 5 +- .../KnowledgeIndexListCommandTests.cs | 7 ++- .../KnowledgeIndexSchemaCommandTests.cs | 7 ++- .../ModelDeploymentCommandTests.cs | 3 +- .../ModelsListCommandTests.cs | 3 +- .../FunctionApp/FunctionAppGetCommand.cs | 5 +- .../FunctionApp/FunctionAppGetCommandTests.cs | 15 ++--- .../WorkspaceListCommandTests.cs | 3 +- .../CertificateCreateCommandTests.cs | 7 ++- .../Certificate/CertificateGetCommandTests.cs | 7 ++- .../CertificateImportCommandTests.cs | 19 ++++--- .../CertificateListCommandTests.cs | 3 +- .../Key/KeyCreateCommandTests.cs | 5 +- .../Key/KeyGetCommandTests.cs | 7 ++- .../Key/KeyListCommandTests.cs | 3 +- .../Secret/SecretCreateCommandTests.cs | 7 ++- .../Secret/SecretGetCommandTests.cs | 7 ++- .../Secret/SecretListCommandTests.cs | 3 +- .../src/Commands/BaseClusterCommand.cs | 3 +- .../ClusterGetCommandTests.cs | 5 +- .../ClusterListCommandTests.cs | 3 +- .../DatabaseListCommandTests.cs | 7 ++- .../QueryCommandTests.cs | 5 +- .../SampleCommandTests.cs | 5 +- .../TableListCommandTests.cs | 5 +- .../TableSchemaCommandTests.cs | 7 ++- .../TestCreateCommandTests.cs | 7 ++- .../TestGetCommandTests.cs | 7 ++- .../TestResourceCreateTests.cs | 3 +- .../TestResourcesListCommandTests.cs | 3 +- .../TestRunCreateCommandTests.cs | 9 +-- .../TestRunGetCommandTests.cs | 7 ++- .../TestRunListCommandTests.cs | 7 ++- .../TestRunUpdateCommandTests.cs | 5 +- .../src/Commands/Product/ProductGetCommand.cs | 6 +- .../Commands/Product/ProductListCommand.cs | 6 +- .../ProductListCommandTests.cs | 2 +- .../Product/ProductGetCommandTests.cs | 7 ++- .../Product/ProductListCommandTests.cs | 21 +++---- .../Entity/EntityGetHealthCommand.cs | 7 ++- .../Commands/Metrics/MetricsQueryCommand.cs | 7 ++- .../src/Services/MonitorHealthModelService.cs | 2 +- .../Entity/EntityGetHealthCommandTests.cs | 15 ++--- .../Log/ResourceLogQueryCommandTests.cs | 13 +++-- .../Log/WorkspaceLogQueryCommandTests.cs | 11 ++-- .../Metrics/MetricsDefinitionsCommandTests.cs | 29 +++++----- .../Metrics/MetricsQueryCommandTests.cs | 29 +++++----- .../Table/TableListCommandTests.cs | 11 ++-- .../TableType/TableTypeListCommandTests.cs | 11 ++-- .../Workspace/WorkspaceListCommandTests.cs | 9 +-- .../src/Services/MySqlService.cs | 2 +- .../Database/DatabaseListCommandTests.cs | 7 ++- .../Database/DatabaseQueryCommandTests.cs | 5 +- .../Server/ServerConfigGetCommandTests.cs | 5 +- .../Server/ServerListCommandTests.cs | 5 +- .../Server/ServerParamGetCommandTests.cs | 5 +- .../Server/ServerParamSetCommandTests.cs | 5 +- .../Table/TableListCommandTests.cs | 5 +- .../Table/TableSchemaGetCommandTests.cs | 5 +- .../src/Services/PostgresService.cs | 2 +- .../Database/DatabaseListCommandTests.cs | 9 +-- .../Database/DatabaseQueryCommandTests.cs | 7 ++- .../Server/ServerConfigGetCommandTests.cs | 7 ++- .../Server/ServerListCommandTests.cs | 7 ++- .../Server/ServerParamGetCommandTests.cs | 7 ++- .../Server/ServerParamSetCommandTests.cs | 7 ++- .../Table/TableListCommandTests.cs | 7 ++- .../Table/TableSchemaGetCommandTests.cs | 7 ++- .../Region/AvailabilityListCommandTests.cs | 21 +++---- .../Commands/Usage/CheckCommandTests.cs | 21 +++---- .../AccessPolicyListCommandTests.cs | 7 ++- .../CacheForRedis/CacheListCommandTests.cs | 7 ++- .../ManagedRedis/ClusterListCommandTests.cs | 7 ++- .../ManagedRedis/DatabaseListCommandTests.cs | 7 ++- .../src/Services/ResourceHealthService.cs | 6 +- .../AvailabilityStatusGetCommandTests.cs | 7 ++- .../AvailabilityStatusListCommandTests.cs | 9 +-- .../ServiceHealthEventsListCommandTests.cs | 9 +-- .../Index/IndexGetCommandTests.cs | 9 +-- .../Index/IndexQueryCommandTests.cs | 7 ++- .../Service/ServiceListCommandTests.cs | 3 +- .../src/Commands/Queue/QueueDetailsCommand.cs | 5 +- .../src/Commands/Queue/QueuePeekCommand.cs | 5 +- .../Topic/SubscriptionDetailsCommand.cs | 5 +- .../Commands/Topic/SubscriptionPeekCommand.cs | 5 +- .../src/Commands/Topic/TopicDetailsCommand.cs | 5 +- .../Queue/QueueDetailsCommandTests.cs | 9 +-- .../Topic/SubscriptionDetailsCommandTests.cs | 9 +-- .../Topic/TopicDetailsCommandTests.cs | 9 +-- .../Database/DatabaseCreateCommand.cs | 9 +-- .../Database/DatabaseDeleteCommand.cs | 5 +- .../Commands/Database/DatabaseListCommand.cs | 5 +- .../Commands/Database/DatabaseShowCommand.cs | 11 ++-- .../Database/DatabaseUpdateCommand.cs | 7 ++- .../ElasticPool/ElasticPoolListCommand.cs | 5 +- .../EntraAdmin/EntraAdminListCommand.cs | 5 +- .../FirewallRule/FirewallRuleCreateCommand.cs | 11 ++-- .../FirewallRule/FirewallRuleDeleteCommand.cs | 11 ++-- .../FirewallRule/FirewallRuleListCommand.cs | 5 +- .../Commands/Server/ServerCreateCommand.cs | 13 +++-- .../Commands/Server/ServerDeleteCommand.cs | 15 ++--- .../src/Commands/Server/ServerShowCommand.cs | 13 +++-- .../src/Services/SqlService.cs | 13 +++-- .../SqlCommandTests.cs | 3 +- .../Database/DatabaseCreateCommandTests.cs | 25 +++++---- .../Database/DatabaseDeleteCommandTests.cs | 25 +++++---- .../Database/DatabaseListCommandTests.cs | 9 +-- .../Database/DatabaseShowCommandTests.cs | 11 ++-- .../Database/DatabaseUpdateCommandTests.cs | 25 +++++---- .../ElasticPoolListCommandTests.cs | 17 +++--- .../EntraAdmin/EntraAdminListCommandTests.cs | 15 ++--- .../FirewallRuleCreateCommandTests.cs | 23 ++++---- .../FirewallRuleDeleteCommandTests.cs | 23 ++++---- .../FirewallRuleListCommandTests.cs | 17 +++--- .../Server/ServerCreateCommandTests.cs | 13 +++-- .../Server/ServerDeleteCommandTests.cs | 19 ++++--- .../Server/ServerShowCommandTests.cs | 19 ++++--- .../Commands/Account/AccountCreateCommand.cs | 5 +- .../Account/AccountCreateCommandTests.cs | 17 +++--- .../Account/AccountGetCommandTests.cs | 15 ++--- .../Commands/Hostpool/BaseHostPoolCommand.cs | 5 +- .../SessionHost/SessionHostListCommand.cs | 5 +- .../SessionHostUserSessionListCommand.cs | 5 +- .../Hostpool/HostpoolListCommandTests.cs | 17 +++--- .../SessionHostListCommandTests.cs | 17 +++--- .../SessionHostUserSessionListCommandTests.cs | 23 ++++---- .../CreateWorkbooksCommandTests.cs | 15 ++--- .../DeleteWorkbooksCommandTests.cs | 15 ++--- .../ListWorkbooksCommandTests.cs | 27 ++++----- .../ShowWorkbooksCommandTests.cs | 13 +++-- .../UpdateWorkbooksCommandTests.cs | 17 +++--- .../BestPractices/GetBestPracticesCommand.cs | 5 +- .../GetWorkloadDefinitionCommand.cs | 5 +- .../PublicApis/GetWorkloadApisCommand.cs | 7 ++- .../Commands/BestPracticesCommandsTests.cs | 23 ++++---- .../tests/Commands/PublicApisCommandsTests.cs | 20 +++---- 206 files changed, 1088 insertions(+), 871 deletions(-) diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs index da8c665a29..9630bcdc62 100644 --- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs +++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs @@ -162,7 +162,7 @@ public override async Task ExecuteAsync(CommandContext context, ErrorMessage = $"Invalid mode '{mode}'. Valid modes are: {ModeTypes.SingleToolProxy}, {ModeTypes.NamespaceProxy}, {ModeTypes.All}." }; - SetValidationError(commandResponse, result.ErrorMessage!, 400); + SetValidationError(commandResponse, result.ErrorMessage!, HttpStatusCode.BadRequest); return result; } @@ -185,7 +185,7 @@ public override async Task ExecuteAsync(CommandContext context, ErrorMessage = $"Invalid transport '{transport}'. Valid transports are: {TransportTypes.StdIo}." }; - SetValidationError(commandResponse, result.ErrorMessage!, 400); + SetValidationError(commandResponse, result.ErrorMessage!, HttpStatusCode.BadRequest); return result; } @@ -216,10 +216,26 @@ public override async Task ExecuteAsync(CommandContext context, ErrorMessage = "Using --enable-insecure-transport requires the host to have either Managed Identity or Workload Identity enabled. Please refer to the troubleshooting guidelines here at https://aka.ms/azmcp/troubleshooting." }; - SetValidationError(commandResponse, result.ErrorMessage!, 422); + SetValidationError(commandResponse, result.ErrorMessage!, HttpStatusCode.InternalServerError); return result; } + /// + /// Provides custom error messages for specific exception types to improve user experience. + /// + /// The exception to format an error message for. + /// A user-friendly error message. + protected override string GetErrorMessage(Exception ex) => ex switch + { + ArgumentException argEx when argEx.Message.Contains("Invalid transport") => + "Invalid transport option specified. Use --transport stdio for the supported transport mechanism.", + ArgumentException argEx when argEx.Message.Contains("Invalid mode") => + "Invalid mode option specified. Use --mode single, namespace, or all for the supported modes.", + InvalidOperationException invOpEx when invOpEx.Message.Contains("Using --enable-insecure-transport") => + "Insecure transport configuration error. Ensure proper authentication configured with Managed Identity or Workload Identity.", + _ => base.GetErrorMessage(ex) + }; + /// /// Creates the host for the MCP server with the specified options. /// @@ -381,13 +397,4 @@ private static string GetSafeAspNetCoreUrl() return url; } - - /// - /// Hosted service for running the MCP server using standard input/output. - /// - private sealed class StdioMcpServerHostedService(IMcpServer session) : BackgroundService - { - /// - protected override Task ExecuteAsync(CancellationToken stoppingToken) => session.RunAsync(stoppingToken); - } } diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoader.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoader.cs index fc2d29c86d..ceff9bff3c 100644 --- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoader.cs +++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoader.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics; +using System.Net; using System.Text.Json.Nodes; using Azure.Mcp.Core.Areas.Server.Models; using Azure.Mcp.Core.Commands; @@ -196,7 +197,7 @@ public async ValueTask CallToolHandler(RequestContext= 300; + var isError = commandResponse.Status < HttpStatusCode.OK || commandResponse.Status >= HttpStatusCode.Ambiguous; return new CallToolResult { diff --git a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs index d8a7df8bc5..b5c5ae7fbf 100644 --- a/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs +++ b/core/Azure.Mcp.Core/src/Commands/BaseCommand.cs @@ -3,6 +3,7 @@ using System.CommandLine.Parsing; using System.Diagnostics; +using System.Net; using Azure.Mcp.Core.Exceptions; using Azure.Mcp.Core.Helpers; using static Azure.Mcp.Core.Services.Telemetry.TelemetryConstants; @@ -12,7 +13,6 @@ namespace Azure.Mcp.Core.Commands; public abstract class BaseCommand : IBaseCommand where TOptions : class, new() { private const string MissingRequiredOptionsPrefix = "Missing Required options: "; - private const int ValidationErrorStatusCode = 400; private const string TroubleshootingUrl = "https://aka.ms/azmcp/troubleshooting"; private readonly Command _command; @@ -83,11 +83,11 @@ protected virtual void HandleException(CommandContext context, Exception ex) protected virtual string GetErrorMessage(Exception ex) => ex.Message; - protected virtual int GetStatusCode(Exception ex) => ex switch + protected virtual HttpStatusCode GetStatusCode(Exception ex) => ex switch { - ArgumentException => 400, // Bad Request for invalid arguments - InvalidOperationException => 422, // Unprocessable Entity for configuration errors - _ => 500 // Internal Server Error for unexpected errors + ArgumentException => HttpStatusCode.BadRequest, // Bad Request for invalid arguments + InvalidOperationException => HttpStatusCode.UnprocessableEntity, // Unprocessable Entity for configuration errors + _ => HttpStatusCode.InternalServerError // Internal Server Error for unexpected errors }; public virtual ValidationResult Validate(CommandResult commandResult, CommandResponse? commandResponse = null) @@ -126,7 +126,7 @@ static void SetValidationError(CommandResponse? response, string errorMessage) { if (response != null) { - response.Status = ValidationErrorStatusCode; + response.Status = HttpStatusCode.BadRequest; response.Message = errorMessage; } } @@ -138,7 +138,7 @@ static void SetValidationError(CommandResponse? response, string errorMessage) /// The command response to update. /// The error message. /// The HTTP status code (defaults to ValidationErrorStatusCode). - protected static void SetValidationError(CommandResponse? response, string errorMessage, int statusCode) + protected static void SetValidationError(CommandResponse? response, string errorMessage, HttpStatusCode statusCode) { if (response != null) { diff --git a/core/Azure.Mcp.Core/src/Commands/CommandFactory.cs b/core/Azure.Mcp.Core/src/Commands/CommandFactory.cs index 7f9fae0838..624b2fabb9 100644 --- a/core/Azure.Mcp.Core/src/Commands/CommandFactory.cs +++ b/core/Azure.Mcp.Core/src/Commands/CommandFactory.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics; +using System.Net; using System.Reflection; using System.Text.Encodings.Web; using System.Text.Json.Serialization; @@ -186,7 +187,7 @@ private void ConfigureCommandHandler(Command command, IBaseCommand implementatio if (!validation.IsValid) { Console.WriteLine(JsonSerializer.Serialize(cmdContext.Response, _srcGenWithOptions.CommandResponse)); - return cmdContext.Response.Status; + return (int)cmdContext.Response.Status; } var response = await implementation.ExecuteAsync(cmdContext, parseResult); @@ -195,7 +196,7 @@ private void ConfigureCommandHandler(Command command, IBaseCommand implementatio var endTime = DateTime.UtcNow; response.Duration = (long)(endTime - startTime).TotalMilliseconds; - if (response.Status == 200 && response.Results == null) + if (response.Status == HttpStatusCode.OK && response.Results == null) { response.Results = ResponseResult.Create(new List(), JsonSourceGenerationContext.Default.ListString); } @@ -206,13 +207,12 @@ private void ConfigureCommandHandler(Command command, IBaseCommand implementatio Console.WriteLine(JsonSerializer.Serialize(response, _srcGenWithOptions.CommandResponse)); } - var status = response.Status; - if (status < 200 || status >= 300) + if (response.Status < HttpStatusCode.OK || response.Status >= HttpStatusCode.Ambiguous) { activity?.SetStatus(ActivityStatusCode.Error).AddTag(TagName.ErrorDetails, response.Message); } - return status; + return (int)response.Status; } catch (Exception ex) { diff --git a/core/Azure.Mcp.Core/src/Commands/GlobalCommand.cs b/core/Azure.Mcp.Core/src/Commands/GlobalCommand.cs index 07ff177981..509272309a 100644 --- a/core/Azure.Mcp.Core/src/Commands/GlobalCommand.cs +++ b/core/Azure.Mcp.Core/src/Commands/GlobalCommand.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics.CodeAnalysis; +using System.Net; using Azure.Core; using Azure.Identity; using Azure.Mcp.Core.Models.Option; @@ -127,11 +128,11 @@ protected override TOptions BindOptions(ParseResult parseResult) _ => ex.Message // Just return the actual exception message }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - AuthenticationFailedException => 401, - RequestFailedException rfEx => rfEx.Status, - HttpRequestException => 503, - _ => 500 + AuthenticationFailedException => HttpStatusCode.Unauthorized, + RequestFailedException rfEx => (HttpStatusCode)rfEx.Status, + HttpRequestException => HttpStatusCode.ServiceUnavailable, + _ => HttpStatusCode.InternalServerError }; } diff --git a/core/Azure.Mcp.Core/src/Exceptions/CommandValidationException.cs b/core/Azure.Mcp.Core/src/Exceptions/CommandValidationException.cs index 1f045b2d6d..f6d0668dc1 100644 --- a/core/Azure.Mcp.Core/src/Exceptions/CommandValidationException.cs +++ b/core/Azure.Mcp.Core/src/Exceptions/CommandValidationException.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; + namespace Azure.Mcp.Core.Exceptions; /// @@ -11,7 +13,7 @@ public class CommandValidationException : Exception { public CommandValidationException( string message, - int statusCode = 400, + HttpStatusCode statusCode = HttpStatusCode.InternalServerError, string? code = null, IReadOnlyList? missingOptions = null) : base(message) { @@ -21,9 +23,9 @@ public CommandValidationException( } /// - /// HTTP-like status code to return in the response. Defaults to 400. + /// HTTP status code to return in the response. Defaults to InternalServerError (500). /// - public int StatusCode { get; } + public HttpStatusCode StatusCode { get; } /// /// Optional machine-readable error code. diff --git a/core/Azure.Mcp.Core/src/Models/Command/CommandContext.cs b/core/Azure.Mcp.Core/src/Models/Command/CommandContext.cs index 5096afa6dc..2c3322f8a6 100644 --- a/core/Azure.Mcp.Core/src/Models/Command/CommandContext.cs +++ b/core/Azure.Mcp.Core/src/Models/Command/CommandContext.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics; +using System.Net; using Microsoft.Extensions.DependencyInjection; namespace Azure.Mcp.Core.Models.Command; @@ -36,7 +37,7 @@ public CommandContext(IServiceProvider serviceProvider, Activity? activity = def Activity = activity; Response = new CommandResponse { - Status = 200, + Status = HttpStatusCode.OK, Message = "Success" }; } diff --git a/core/Azure.Mcp.Core/src/Models/Command/CommandResponse.cs b/core/Azure.Mcp.Core/src/Models/Command/CommandResponse.cs index b3e6f093cb..7145a1f546 100644 --- a/core/Azure.Mcp.Core/src/Models/Command/CommandResponse.cs +++ b/core/Azure.Mcp.Core/src/Models/Command/CommandResponse.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -9,7 +10,8 @@ namespace Azure.Mcp.Core.Models.Command; public class CommandResponse { [JsonPropertyName("status")] - public int Status { get; set; } + [JsonConverter(typeof(HttpStatusCodeConverter))] + public HttpStatusCode Status { get; set; } = HttpStatusCode.OK; [JsonPropertyName("message")] public string Message { get; set; } = string.Empty; @@ -58,3 +60,20 @@ public override void Write(Utf8JsonWriter writer, ResponseResult? value, JsonSer value?.Write(writer); } } + +internal class HttpStatusCodeConverter : JsonConverter +{ + public override HttpStatusCode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Number) + { + return (HttpStatusCode)reader.GetInt32(); + } + throw new JsonException("Expected a number for HttpStatusCode."); + } + + public override void Write(Utf8JsonWriter writer, HttpStatusCode value, JsonSerializerOptions options) + { + writer.WriteNumberValue((int)value); + } +} diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Group/UnitTests/GroupListCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Group/UnitTests/GroupListCommandTests.cs index 6db42008be..b677e47b43 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Group/UnitTests/GroupListCommandTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Group/UnitTests/GroupListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Areas.Group.Commands; using Azure.Mcp.Core.Models.Command; @@ -64,7 +65,7 @@ public async Task ExecuteAsync_WithValidSubscription_ReturnsResourceGroups() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); var jsonDoc = JsonDocument.Parse(JsonSerializer.Serialize(result.Results)); @@ -114,7 +115,7 @@ public async Task ExecuteAsync_WithTenant_PassesTenantToService() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); await _resourceGroupService.Received(1).GetResourceGroups( Arg.Is(x => x == subscriptionId), Arg.Is(x => x == tenantId), @@ -137,7 +138,7 @@ public async Task ExecuteAsync_EmptyResourceGroupList_ReturnsNullResults() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.Null(result.Results); } @@ -158,7 +159,7 @@ public async Task ExecuteAsync_ServiceThrowsException_ReturnsErrorInResponse() // Assert Assert.NotNull(result); - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.Contains(expectedError, result.Message); } } diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/CommandGroupDiscoveryStrategyTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/CommandGroupDiscoveryStrategyTests.cs index ddd00ba5e2..81c09dbc2a 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/CommandGroupDiscoveryStrategyTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/CommandGroupDiscoveryStrategyTests.cs @@ -516,7 +516,7 @@ public async Task DiscoverServersAsync_WithNamespaceFilter_ReturnsOnlySpecifiedN // Arrange var options = new ServiceStartOptions { - Namespace = new[] { "storage", "keyvault" } + Namespace = ["storage", "keyvault"] }; var strategy = CreateStrategy(options: options); diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/RegistryServerProviderTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/RegistryServerProviderTests.cs index c9b938623c..6e51f48022 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/RegistryServerProviderTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/RegistryServerProviderTests.cs @@ -89,7 +89,7 @@ public async Task CreateClientAsync_WithUrlReturning404_ThrowsHttpRequestExcepti var exception = await Assert.ThrowsAsync( () => provider.CreateClientAsync(new McpClientOptions())); - Assert.Contains("404", exception.Message); + Assert.Contains(((int)HttpStatusCode.NotFound).ToString(), exception.Message); } [Fact] @@ -216,11 +216,11 @@ public MockHttpTestServer() var context = await _listener.GetContextAsync(); if (context.Request.Url?.AbsolutePath == "/mcp") { - context.Response.StatusCode = 404; + context.Response.StatusCode = (int)HttpStatusCode.NotFound; } else { - context.Response.StatusCode = 400; + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } context.Response.Close(); } diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Runtime/McpRuntimeTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Runtime/McpRuntimeTests.cs index cb72faefbc..b56c3f7a72 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Runtime/McpRuntimeTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Runtime/McpRuntimeTests.cs @@ -164,7 +164,7 @@ public void Constructor_LogsInitializationInformation() var options = CreateOptions(new ServiceStartOptions { ReadOnly = true, - Namespace = new[] { "storage", "keyvault" } + Namespace = ["storage", "keyvault"] }); // Act @@ -410,7 +410,7 @@ public void Constructor_WithDifferentServiceOptions_LogsCorrectly() var options2 = CreateOptions(new ServiceStartOptions { ReadOnly = null, - Namespace = new[] { "storage", "keyvault", "monitor" } + Namespace = ["storage", "keyvault", "monitor"] }); var runtime2 = new McpRuntime(mockToolLoader, options2, CreateMockTelemetryService(), logger); Assert.NotNull(runtime2); diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ServiceCollectionExtensionsTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ServiceCollectionExtensionsTests.cs index 900c78888e..619f0b8a36 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ServiceCollectionExtensionsTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ServiceCollectionExtensionsTests.cs @@ -247,7 +247,7 @@ public void AddAzureMcpServer_WithSpecificServiceAreas_RegistersCompositeToolLoa var options = new ServiceStartOptions { Transport = StdioTransport, - Namespace = new[] { "keyvault", "storage" } + Namespace = ["keyvault", "storage"] }; // Act @@ -279,7 +279,7 @@ public void AddAzureMcpServer_WithSingleServiceArea_RegistersAppropriateServices var options = new ServiceStartOptions { Transport = StdioTransport, - Namespace = new[] { serviceArea } + Namespace = [serviceArea] }; // Act diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoaderTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoaderTests.cs index 5d1a394268..247e0b5647 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoaderTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoaderTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Areas.Server.Commands.ToolLoading; using Azure.Mcp.Core.Commands; @@ -104,7 +105,7 @@ public async Task ListToolsHandler_WithServiceFilter_ReturnsOnlyFilteredTools() // Try to filter by a specific service/group - using a common Azure service name var filteredOptions = new ToolLoaderOptions { - Namespace = new[] { "storage" } // Assuming there's a storage service group + Namespace = ["storage"] // Assuming there's a storage service group }; var (toolLoader, _) = CreateToolLoader(filteredOptions); var request = CreateRequest(); @@ -143,7 +144,7 @@ public async Task ListToolsHandler_WithMultipleServiceFilters_ReturnsToolsFromAl // Try to filter by multiple real service/group names from the codebase var multiServiceOptions = new ToolLoaderOptions { - Namespace = new[] { "storage", "appconfig", "search" } // Real Azure service groups from the codebase + Namespace = ["storage", "appconfig", "search"] // Real Azure service groups from the codebase }; var (toolLoader, commandFactory) = CreateToolLoader(multiServiceOptions); var request = CreateRequest(); @@ -297,7 +298,7 @@ public async Task GetsToolsWithRawMcpInputOption() { var filteredOptions = new ToolLoaderOptions { - Namespace = new[] { "deploy" } // Assuming there's a deploy service group + Namespace = ["deploy"] // Assuming there's a deploy service group }; var (toolLoader, _) = CreateToolLoader(filteredOptions); var request = CreateRequest(); @@ -503,7 +504,7 @@ public async Task CallToolHandler_WithSecretTool_WhenClientDoesNotSupportElicita fakeCommand.Title.Returns("Fake Secret Get"); fakeCommand.Metadata.Returns(new ToolMetadata { Secret = true }); fakeCommand.ExecuteAsync(Arg.Any(), Arg.Any()) - .Returns(new CommandResponse { Status = 200, Message = "Secret test response" }); + .Returns(new CommandResponse { Status = HttpStatusCode.OK, Message = "Secret test response" }); // Add our fake command to the internal command map using reflection var commandMapField = typeof(CommandFactory).GetField("_commandMap", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); @@ -543,7 +544,7 @@ public async Task CallToolHandler_WithNonSecretTool_DoesNotTriggerElicitation() fakeCommand.Title.Returns("Fake Non-Secret Get"); fakeCommand.Metadata.Returns(new ToolMetadata { Secret = false }); // Not secret fakeCommand.ExecuteAsync(Arg.Any(), Arg.Any()) - .Returns(new CommandResponse { Status = 200, Message = "Test response" }); + .Returns(new CommandResponse { Status = HttpStatusCode.OK, Message = "Test response" }); // Add our fake command to the internal command map using reflection var commandMapField = typeof(CommandFactory).GetField("_commandMap", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); @@ -585,7 +586,7 @@ public async Task CallToolHandler_WithSecretTool_WhenInsecureDisableElicitationE fakeCommand.Title.Returns("Fake Secret Get"); fakeCommand.Metadata.Returns(new ToolMetadata { Secret = true }); fakeCommand.ExecuteAsync(Arg.Any(), Arg.Any()) - .Returns(new CommandResponse { Status = 200, Message = "Secret test response" }); + .Returns(new CommandResponse { Status = HttpStatusCode.OK, Message = "Secret test response" }); // Add our fake command to the internal command map using reflection var commandMapField = typeof(CommandFactory).GetField("_commandMap", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); @@ -612,7 +613,7 @@ public async Task CallToolHandler_WithSecretTool_WhenInsecureDisableElicitationE Assert.False(result.IsError); var responseText = ((TextContentBlock)result.Content.First()).Text; var response = JsonSerializer.Deserialize(responseText); - Assert.Equal(200, response!.Status); + Assert.Equal(HttpStatusCode.OK, response!.Status); Assert.Equal("Secret test response", response.Message); } @@ -630,7 +631,7 @@ public async Task CallToolHandler_WithSecretTool_WhenInsecureDisableElicitationD fakeCommand.Title.Returns("Fake Secret Get"); fakeCommand.Metadata.Returns(new ToolMetadata { Secret = true }); fakeCommand.ExecuteAsync(Arg.Any(), Arg.Any()) - .Returns(new CommandResponse { Status = 200, Message = "Secret test response" }); + .Returns(new CommandResponse { Status = HttpStatusCode.OK, Message = "Secret test response" }); // Add our fake command to the internal command map using reflection var commandMapField = typeof(CommandFactory).GetField("_commandMap", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs index 4911b55e7c..369b6892cb 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/ServiceStartCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Areas.Server.Commands; using Azure.Mcp.Core.Areas.Server.Options; using Azure.Mcp.Core.Models.Command; @@ -103,9 +104,9 @@ public async Task ExecuteAsync_InvalidTransport_ReturnsValidationError(string in var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains($"Invalid transport '{invalidTransport}'", response.Message); - Assert.Contains("Valid transports are: stdio", response.Message); + Assert.Contains("Valid transports are: stdio.", response.Message); } [Theory] @@ -123,9 +124,9 @@ public async Task ExecuteAsync_InvalidMode_ReturnsValidationError(string invalid var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains($"Invalid mode '{invalidMode}'", response.Message); - Assert.Contains("Valid modes are: single, namespace, all", response.Message); + Assert.Contains("Valid modes are: single, namespace, all.", response.Message); } [Theory] @@ -144,7 +145,7 @@ public async Task ExecuteAsync_ValidMode_DoesNotReturnValidationError(string? va var response = await _command.ExecuteAsync(context, parseResult); // Assert - Should not fail validation, though may fail later due to server startup - if (response.Status == 400 && response.Message?.Contains("Invalid mode") == true) + if (response.Status == HttpStatusCode.BadRequest && response.Message?.Contains("Invalid mode") == true) { Assert.Fail($"Mode '{validMode}' should be valid but got validation error: {response.Message}"); } @@ -285,7 +286,7 @@ public void GetStatusCode_WithArgumentException_Returns400() var statusCode = GetStatusCode(exception); // Assert - Assert.Equal(400, statusCode); + Assert.Equal(HttpStatusCode.BadRequest, statusCode); } [Fact] @@ -298,7 +299,7 @@ public void GetStatusCode_WithInvalidOperationException_Returns422() var statusCode = GetStatusCode(exception); // Assert - Assert.Equal(422, statusCode); + Assert.Equal(HttpStatusCode.UnprocessableEntity, statusCode); } [Fact] @@ -311,7 +312,7 @@ public void GetStatusCode_WithGenericException_Returns500() var statusCode = GetStatusCode(exception); // Assert - Assert.Equal(500, statusCode); + Assert.Equal(HttpStatusCode.InternalServerError, statusCode); } [Fact] @@ -525,7 +526,7 @@ private ServiceStartOptions GetBoundOptions(ParseResult parseResult) // Use reflection to access the protected BindOptions method var method = typeof(ServiceStartCommand).GetMethod("BindOptions", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - return (ServiceStartOptions)method!.Invoke(_command, new object[] { parseResult })!; + return (ServiceStartOptions)method!.Invoke(_command, [parseResult])!; } private string GetErrorMessage(Exception exception) @@ -533,14 +534,14 @@ private string GetErrorMessage(Exception exception) // Use reflection to access the protected GetErrorMessage method var method = typeof(ServiceStartCommand).GetMethod("GetErrorMessage", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - return (string)method!.Invoke(_command, new object[] { exception })!; + return (string)method!.Invoke(_command, [exception])!; } - private int GetStatusCode(Exception exception) + private HttpStatusCode GetStatusCode(Exception exception) { // Use reflection to access the protected GetStatusCode method var method = typeof(ServiceStartCommand).GetMethod("GetStatusCode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - return (int)method!.Invoke(_command, new object[] { exception })!; + return (HttpStatusCode)method!.Invoke(_command, [exception])!; } } diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Subscription/SubscriptionListCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Subscription/SubscriptionListCommandTests.cs index 92aed978e2..256ffa580f 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Subscription/SubscriptionListCommandTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Subscription/SubscriptionListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Areas.Subscription.Commands; using Azure.Mcp.Core.Models; @@ -63,7 +64,7 @@ public async Task ExecuteAsync_NoParameters_ReturnsSubscriptions() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); var jsonDoc = JsonDocument.Parse(JsonSerializer.Serialize(result.Results)); @@ -98,7 +99,7 @@ public async Task ExecuteAsync_WithTenantId_PassesTenantToService() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); await _subscriptionService.Received(1).GetSubscriptions( Arg.Is(x => x == tenantId), Arg.Any()); @@ -119,7 +120,7 @@ public async Task ExecuteAsync_EmptySubscriptionList_ReturnsNullResults() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.Null(result.Results); } @@ -139,7 +140,7 @@ public async Task ExecuteAsync_ServiceThrowsException_ReturnsErrorInResponse() // Assert Assert.NotNull(result); - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.Contains(expectedError, result.Message); } @@ -159,7 +160,7 @@ public async Task ExecuteAsync_WithAuthMethod_PassesAuthMethodToCommand() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); await _subscriptionService.Received(1).GetSubscriptions( Arg.Any(), Arg.Any()); diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs index 92061a9e40..ec1bd73d0d 100644 --- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs +++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Areas; using Azure.Mcp.Core.Areas.Tools.Commands; @@ -18,8 +19,6 @@ namespace Azure.Mcp.Core.UnitTests.Areas.Tools.UnitTests; public class ToolsListCommandTests { - private const int SuccessStatusCode = 200; - private const int ErrorStatusCode = 500; private const int MinimumExpectedCommands = 3; private readonly IServiceProvider _serviceProvider; @@ -195,7 +194,7 @@ public async Task ExecuteAsync_WithNullServiceProvider_HandlesGracefully() // Assert Assert.NotNull(response); - Assert.Equal(ErrorStatusCode, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("cannot be null", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -219,7 +218,7 @@ public async Task ExecuteAsync_WithCorruptedCommandFactory_HandlesGracefully() // Assert Assert.NotNull(response); - Assert.Equal(ErrorStatusCode, response.Status); + Assert.Equal(HttpStatusCode.UnprocessableEntity, response.Status); Assert.Contains("Corrupted command factory", response.Message); } @@ -345,7 +344,7 @@ public async Task ExecuteAsync_WithEmptyCommandFactory_ReturnsEmptyResults() // Assert Assert.NotNull(response); - Assert.Equal(SuccessStatusCode, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var result = DeserializeResults(response.Results!); diff --git a/docs/new-command.md b/docs/new-command.md index 211d7e8ebf..02366298c1 100644 --- a/docs/new-command.md +++ b/docs/new-command.md @@ -477,6 +477,8 @@ protected override MyCommandOptions BindOptions(ParseResult parseResult) ### 3. Command Class ```csharp +using System.Net; + public sealed class {Resource}{Operation}Command(ILogger<{Resource}{Operation}Command> logger) : Base{Toolset}Command<{Resource}{Operation}Options> { @@ -568,18 +570,18 @@ public sealed class {Resource}{Operation}Command(ILogger<{Resource}{Operation}Co // Implementation-specific error handling, only implement if this differs from base class behavior protected override string GetErrorMessage(Exception ex) => ex switch { - Azure.RequestFailedException reqEx when reqEx.Status == 404 => + Azure.RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "Resource not found. Verify the resource exists and you have access.", - Azure.RequestFailedException reqEx when reqEx.Status == 403 => + Azure.RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the resource. Details: {reqEx.Message}", Azure.RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) }; // Implementation-specific status code retrieval, only implement if this differs from base class behavior - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - Azure.RequestFailedException reqEx => reqEx.Status, + Azure.RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, _ => base.GetStatusCode(ex) }; @@ -870,7 +872,7 @@ public class {Resource}{Operation}CommandTests var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -895,7 +897,7 @@ public class {Resource}{Operation}CommandTests var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -918,7 +920,7 @@ public class {Resource}{Operation}CommandTests var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -1007,7 +1009,7 @@ public class {Toolset}CommandTests(ITestOutputHelper output) Guidelines: - When validating JSON for an expected property use `JsonElement.AssertProperty`. -- When validating JSON for a conditional property use `JsonElement.TryGetProperty` in an if-clause. +- When validating JSON for a conditional property use `JsonElement.TryGetProperty` in an if-clause. ### 8. Command Registration @@ -1086,19 +1088,19 @@ Guidelines: Commands in Azure MCP follow a standardized error handling approach using the base `HandleException` method inherited from `BaseCommand`. Here are the key aspects: ### 1. Status Code Mapping -The base implementation returns 500 for all exceptions by default: +The base implementation returns InternalServerError for all exceptions by default: ```csharp -protected virtual int GetStatusCode(Exception ex) => 500; +protected virtual HttpStatusCode GetStatusCode(Exception ex) => HttpStatusCode.InternalServerError; ``` Commands should override this to provide appropriate status codes: ```csharp -protected override int GetStatusCode(Exception ex) => ex switch +protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - Azure.RequestFailedException reqEx => reqEx.Status, // Use Azure-reported status - Azure.Identity.AuthenticationFailedException => 401, // Unauthorized - ValidationException => 400, // Bad request - _ => base.GetStatusCode(ex) // Fall back to 500 + Azure.RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, // Use Azure-reported status + Azure.Identity.AuthenticationFailedException => HttpStatusCode.Unauthorized, // Unauthorized + ValidationException => HttpStatusCode.BadRequest, // Bad request + _ => base.GetStatusCode(ex) // Fall back to InternalServerError }; ``` @@ -1114,9 +1116,9 @@ protected override string GetErrorMessage(Exception ex) => ex switch { Azure.Identity.AuthenticationFailedException authEx => $"Authentication failed. Please run 'az login' to sign in. Details: {authEx.Message}", - Azure.RequestFailedException reqEx when reqEx.Status == 404 => + Azure.RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "Resource not found. Verify the resource name and that you have access.", - Azure.RequestFailedException reqEx when reqEx.Status == 403 => + Azure.RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Access denied. Ensure you have appropriate RBAC permissions. Details: {reqEx.Message}", Azure.RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) @@ -1210,7 +1212,7 @@ public async Task ExecuteAsync_ValidatesInput( string args, bool shouldSucceed, string expectedError) { var response = await ExecuteCommand(args); - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (!shouldSucceed) Assert.Contains(expectedError, response.Message); } @@ -1226,7 +1228,7 @@ public async Task ExecuteAsync_HandlesServiceError() var response = await ExecuteCommand("--param value"); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -1433,7 +1435,7 @@ public class {Toolset}CommandTests( ITestOutputHelper output) }); // Should return validation error - Assert.NotEqual(200, result.Status); + Assert.NotEqual(HttpStatusCode.OK, result.Status); } } ``` @@ -1463,7 +1465,7 @@ public async Task Should_HandleAuth(AuthMethod method) { "auth-method", method.ToString() } }); // Verify auth worked - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); } [Theory] @@ -1472,7 +1474,7 @@ public async Task Should_HandleAuth(AuthMethod method) public async Task Should_Return400_ForInvalidInput(string args) { var result = await CallCommand(args); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Contains("validation", result.Message.ToLower()); } ``` @@ -1705,9 +1707,9 @@ protected override MyOptions BindOptions(ParseResult parseResult) ```csharp protected override string GetErrorMessage(Exception ex) => ex switch { - Azure.RequestFailedException reqEx when reqEx.Status == 404 => + Azure.RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "Resource not found. Verify the resource exists and you have access.", - Azure.RequestFailedException reqEx when reqEx.Status == 403 => + Azure.RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed. Details: {reqEx.Message}", _ => base.GetErrorMessage(ex) }; @@ -1734,9 +1736,9 @@ catch (Exception ex) - Handle all exceptions 2. Error Handling: - - Return 400 for validation errors - - Return 401 for authentication failures - - Return 500 for unexpected errors + - Return HttpStatusCode.BadRequest for validation errors + - Return HttpStatusCode.Unauthorized for authentication failures + - Return HttpStatusCode.InternalServerError for unexpected errors - Return service-specific status codes from RequestFailedException - Add troubleshooting URL to error messages - Log errors with context information diff --git a/eng/tools/ToolDescriptionEvaluator/Services/EmbeddingService.cs b/eng/tools/ToolDescriptionEvaluator/Services/EmbeddingService.cs index bfbb126f2c..9dd18f8852 100644 --- a/eng/tools/ToolDescriptionEvaluator/Services/EmbeddingService.cs +++ b/eng/tools/ToolDescriptionEvaluator/Services/EmbeddingService.cs @@ -17,7 +17,7 @@ public async Task CreateEmbeddingsAsync(string input) { var requestBody = new EmbeddingRequest { - Input = new[] { input } + Input = [input] }; var json = JsonSerializer.Serialize(requestBody, SourceGenerationContext.Default.EmbeddingRequest); diff --git a/servers/Azure.Mcp.Server/src/Program.cs b/servers/Azure.Mcp.Server/src/Program.cs index efd45dfd63..8a35bd88fd 100644 --- a/servers/Azure.Mcp.Server/src/Program.cs +++ b/servers/Azure.Mcp.Server/src/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Areas; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Services.Azure.ResourceGroup; @@ -45,7 +46,7 @@ private static async Task Main(string[] args) { WriteResponse(new CommandResponse { - Status = 500, + Status = HttpStatusCode.InternalServerError, Message = ex.Message, Duration = 0 }); diff --git a/servers/Fabric.Mcp.Server/src/Program.cs b/servers/Fabric.Mcp.Server/src/Program.cs index f8a378a3df..7d9dab3baf 100644 --- a/servers/Fabric.Mcp.Server/src/Program.cs +++ b/servers/Fabric.Mcp.Server/src/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Areas; using Azure.Mcp.Core.Commands; @@ -49,7 +50,7 @@ private static async Task Main(string[] args) { WriteResponse(new CommandResponse { - Status = 500, + Status = HttpStatusCode.InternalServerError, Message = ex.Message, Duration = 0 }); diff --git a/servers/Template.Mcp.Server/src/Program.cs b/servers/Template.Mcp.Server/src/Program.cs index b1cfc8f798..a3c7c4bef7 100644 --- a/servers/Template.Mcp.Server/src/Program.cs +++ b/servers/Template.Mcp.Server/src/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Areas; using Azure.Mcp.Core.Commands; @@ -46,7 +47,7 @@ private static async Task Main(string[] args) { WriteResponse(new CommandResponse { - Status = 500, + Status = HttpStatusCode.InternalServerError, Message = ex.Message, Duration = 0 }); diff --git a/tools/Azure.Mcp.Tools.Acr/tests/Azure.Mcp.Tools.Acr.UnitTests/Registry/RegistryListCommandTests.cs b/tools/Azure.Mcp.Tools.Acr/tests/Azure.Mcp.Tools.Acr.UnitTests/Registry/RegistryListCommandTests.cs index 7b50206205..01179860bd 100644 --- a/tools/Azure.Mcp.Tools.Acr/tests/Azure.Mcp.Tools.Acr.UnitTests/Registry/RegistryListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Acr/tests/Azure.Mcp.Tools.Acr.UnitTests/Registry/RegistryListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Helpers; using Azure.Mcp.Core.Models.Command; @@ -71,7 +72,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -95,7 +96,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -114,7 +115,7 @@ public async Task ExecuteAsync_FiltersById_ReturnsFilteredRegistries() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); await _service.Received(1).ListRegistries("sub", "rg", Arg.Any(), Arg.Any()); } @@ -132,7 +133,7 @@ public async Task ExecuteAsync_EmptyList_ReturnsEmptyResults() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -156,7 +157,7 @@ public async Task ExecuteAsync_ReturnsExpectedRegistryProperties() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } } diff --git a/tools/Azure.Mcp.Tools.Acr/tests/Azure.Mcp.Tools.Acr.UnitTests/Registry/RegistryRepositoryListCommandTests.cs b/tools/Azure.Mcp.Tools.Acr/tests/Azure.Mcp.Tools.Acr.UnitTests/Registry/RegistryRepositoryListCommandTests.cs index 7fd1ca1ec5..467aec8d82 100644 --- a/tools/Azure.Mcp.Tools.Acr/tests/Azure.Mcp.Tools.Acr.UnitTests/Registry/RegistryRepositoryListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Acr/tests/Azure.Mcp.Tools.Acr.UnitTests/Registry/RegistryRepositoryListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -59,7 +60,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -83,7 +84,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -101,7 +102,7 @@ public async Task ExecuteAsync_Empty_ReturnsEmptyResults() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); diff --git a/tools/Azure.Mcp.Tools.Aks/src/Commands/Cluster/ClusterGetCommand.cs b/tools/Azure.Mcp.Tools.Aks/src/Commands/Cluster/ClusterGetCommand.cs index 86a87fcda9..3fd49f4962 100644 --- a/tools/Azure.Mcp.Tools.Aks/src/Commands/Cluster/ClusterGetCommand.cs +++ b/tools/Azure.Mcp.Tools.Aks/src/Commands/Cluster/ClusterGetCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models.Option; @@ -86,9 +87,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "AKS cluster not found. Verify the cluster name, resource group, and subscription, and ensure you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the AKS cluster. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.Aks/src/Commands/Cluster/ClusterListCommand.cs b/tools/Azure.Mcp.Tools.Aks/src/Commands/Cluster/ClusterListCommand.cs index b3b8a21249..3def006727 100644 --- a/tools/Azure.Mcp.Tools.Aks/src/Commands/Cluster/ClusterListCommand.cs +++ b/tools/Azure.Mcp.Tools.Aks/src/Commands/Cluster/ClusterListCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Aks.Options.Cluster; using Azure.Mcp.Tools.Aks.Services; @@ -65,9 +66,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "Subscription not found. Verify the subscription ID and you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing AKS clusters. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.Aks/src/Commands/Nodepool/NodepoolGetCommand.cs b/tools/Azure.Mcp.Tools.Aks/src/Commands/Nodepool/NodepoolGetCommand.cs index 7cea6d3ffc..62388bed25 100644 --- a/tools/Azure.Mcp.Tools.Aks/src/Commands/Nodepool/NodepoolGetCommand.cs +++ b/tools/Azure.Mcp.Tools.Aks/src/Commands/Nodepool/NodepoolGetCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models.Option; @@ -89,9 +90,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "AKS node pool not found. Verify the node pool name, cluster, resource group, and subscription, and ensure you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the AKS node pool. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.Aks/src/Commands/Nodepool/NodepoolListCommand.cs b/tools/Azure.Mcp.Tools.Aks/src/Commands/Nodepool/NodepoolListCommand.cs index f0ac2a3006..6535c86451 100644 --- a/tools/Azure.Mcp.Tools.Aks/src/Commands/Nodepool/NodepoolListCommand.cs +++ b/tools/Azure.Mcp.Tools.Aks/src/Commands/Nodepool/NodepoolListCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models.Option; @@ -85,9 +86,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "AKS cluster or node pools not found. Verify the cluster name, resource group, and subscription, and ensure you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing AKS node pools. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterGetCommandTests.cs b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterGetCommandTests.cs index 3072612ea6..a76cd04a7e 100644 --- a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Aks.Commands.Cluster; @@ -77,7 +78,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -121,7 +122,7 @@ public async Task ExecuteAsync_ReturnsClusterWhenFound() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -139,7 +140,7 @@ public async Task ExecuteAsync_ReturnsNullWhenClusterNotFound() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Null(response.Results); Assert.Equal("Success", response.Message); } @@ -157,7 +158,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -166,7 +167,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_Handles404NotFound() { // Arrange - var notFoundException = new RequestFailedException(404, "Not Found"); + var notFoundException = new RequestFailedException((int)HttpStatusCode.NotFound, "Not Found"); _aksService.GetCluster(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(Task.FromException(notFoundException)); @@ -176,7 +177,7 @@ public async Task ExecuteAsync_Handles404NotFound() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("AKS cluster not found", response.Message); } @@ -194,7 +195,7 @@ public async Task ExecuteAsync_Handles403Forbidden() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterListCommandTests.cs b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterListCommandTests.cs index e01e2d7043..814e5fd788 100644 --- a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -67,7 +68,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -97,7 +98,7 @@ public async Task ExecuteAsync_ReturnsClustersList() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the mock was called @@ -235,7 +236,7 @@ public async Task ExecuteAsync_ReturnsEmptyWhenNoClusters() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -259,7 +260,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Nodepool/NodepoolGetCommandTests.cs b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Nodepool/NodepoolGetCommandTests.cs index c36036f034..ae6d59dc29 100644 --- a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Nodepool/NodepoolGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Nodepool/NodepoolGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -73,7 +74,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.Equal("Success", response.Message); @@ -141,7 +142,7 @@ public async Task ExecuteAsync_ReturnsNodePool() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); await _aksService.Received(1).GetNodePool(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); @@ -199,7 +200,7 @@ public async Task ExecuteAsync_ReturnsNullWhenNodePoolNotFound() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Null(response.Results); } @@ -217,7 +218,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Nodepool/NodepoolListCommandTests.cs b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Nodepool/NodepoolListCommandTests.cs index 3aad7a920b..397d8db16e 100644 --- a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Nodepool/NodepoolListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Nodepool/NodepoolListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -70,7 +71,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -170,7 +171,7 @@ public async Task ExecuteAsync_ReturnsNodePoolsList() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the mock was called @@ -231,7 +232,7 @@ public async Task ExecuteAsync_ReturnsEmptyWhenNoNodePools() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -255,7 +256,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/Account/AccountListCommandTests.cs b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/Account/AccountListCommandTests.cs index 7c1d54695a..55976c0e30 100644 --- a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/Account/AccountListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/Account/AccountListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -61,7 +62,7 @@ public async Task ExecuteAsync_ReturnsAccounts_WhenAccountsExist() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -89,7 +90,7 @@ public async Task ExecuteAsync_ReturnsEmpty_WhenNoAccountsExist() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -115,7 +116,7 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Service error", response.Message); } @@ -126,7 +127,7 @@ public async Task ExecuteAsync_Returns400_WhenSubscriptionIsMissing() var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse([])); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -143,7 +144,7 @@ public async Task ExecuteAsync_Returns503_WhenServiceIsUnavailable() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(503, response.Status); + Assert.Equal(HttpStatusCode.ServiceUnavailable, response.Status); Assert.Contains("Service Unavailable", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueDeleteCommandTests.cs index 43b0d60dbf..10a44fd877 100644 --- a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueDeleteCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -53,7 +54,7 @@ public async Task ExecuteAsync_DeletesKeyValue_WhenValidParametersProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).DeleteKeyValue( "account1", "my-key", @@ -84,7 +85,7 @@ public async Task ExecuteAsync_DeletesKeyValueWithLabel_WhenLabelProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).DeleteKeyValue( "account1", "my-key", @@ -123,7 +124,7 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Failed to delete key-value", response.Message); } @@ -140,7 +141,7 @@ public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(param var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueListCommandTests.cs b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueListCommandTests.cs index a0ef3229e9..4b6af0cc47 100644 --- a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -64,7 +65,7 @@ public async Task ExecuteAsync_ReturnsSettings_WhenSettingsExist() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -99,7 +100,7 @@ public async Task ExecuteAsync_ReturnsFilteredSettings_WhenKeyFilterProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -135,7 +136,7 @@ public async Task ExecuteAsync_ReturnsFilteredSettings_WhenLabelFilterProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -166,7 +167,7 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Service error", response.Message); } @@ -182,7 +183,7 @@ public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(param var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueSetCommandTests.cs b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueSetCommandTests.cs index 14b7ad8b3a..4449322253 100644 --- a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueSetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueSetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -54,7 +55,7 @@ public async Task ExecuteAsync_SetsKeyValue_WhenValidParametersProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).SetKeyValue( "account1", "my-key", @@ -90,7 +91,7 @@ public async Task ExecuteAsync_SetsKeyValueWithLabel_WhenLabelProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).SetKeyValue( "account1", "my-key", @@ -128,7 +129,7 @@ public async Task ExecuteAsync_SetsKeyValueWithContentTypeAndTagsProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).SetKeyValue( "account1", "my-key", @@ -179,7 +180,7 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Failed to set key-value", response.Message); } @@ -197,7 +198,7 @@ public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(strin var response = await _command.ExecuteAsync(_context, parsedArgs); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueShowCommandTests.cs b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueShowCommandTests.cs index a9d0513616..a7e3fa2d41 100644 --- a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueShowCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/KeyValueShowCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -72,7 +73,7 @@ public async Task ExecuteAsync_ReturnsSetting_WhenSettingExists() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -114,7 +115,7 @@ public async Task ExecuteAsync_ReturnsSetting_WhenNoLabelProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -148,7 +149,7 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Setting not found", response.Message); } @@ -165,7 +166,7 @@ public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(param var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/Lock/KeyValueLockSetCommandTests.cs b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/Lock/KeyValueLockSetCommandTests.cs index e1aac707a2..56ee131243 100644 --- a/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/Lock/KeyValueLockSetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppConfig/tests/Azure.Mcp.Tools.AppConfig.UnitTests/KeyValue/Lock/KeyValueLockSetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -54,7 +55,7 @@ public async Task ExecuteAsync_LocksKeyValue_WhenValidParametersProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).SetKeyValueLockState( "account1", "my-key", @@ -88,7 +89,7 @@ public async Task ExecuteAsync_LocksKeyValueWithLabel_WhenLabelProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).SetKeyValueLockState( "account1", "my-key", @@ -121,7 +122,7 @@ public async Task ExecuteAsync_UnlocksKeyValue_WhenValidParametersProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).SetKeyValueLockState( "account1", "my-key", @@ -153,7 +154,7 @@ public async Task ExecuteAsync_UnlocksKeyValueWithLabel_WhenLabelProvided() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _appConfigService.Received(1).SetKeyValueLockState( "account1", "my-key", @@ -195,7 +196,7 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException(bool locked var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Failed to lock key-value", response.Message); } @@ -214,7 +215,7 @@ public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(strin var response = await _command.ExecuteAsync(_context, parsedArgs); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.AppLens/tests/Azure.Mcp.Tools.AppLens.UnitTests/Resource/ResourceDiagnoseCommandTests.cs b/tools/Azure.Mcp.Tools.AppLens/tests/Azure.Mcp.Tools.AppLens.UnitTests/Resource/ResourceDiagnoseCommandTests.cs index 8e64dba226..7f453592f1 100644 --- a/tools/Azure.Mcp.Tools.AppLens/tests/Azure.Mcp.Tools.AppLens.UnitTests/Resource/ResourceDiagnoseCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AppLens/tests/Azure.Mcp.Tools.AppLens.UnitTests/Resource/ResourceDiagnoseCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.AppLens.Commands.Resource; @@ -66,7 +67,7 @@ public async Task ExecuteAsync_ReturnsDiagnosticResult_WhenValidParametersProvid var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -98,7 +99,7 @@ public async Task ExecuteAsync_Returns400_WhenQuestionIsMissing() ])); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -114,7 +115,7 @@ public async Task ExecuteAsync_Returns400_WhenResourceIsMissing() ])); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -130,7 +131,7 @@ public async Task ExecuteAsync_Returns400_WhenSubscriptionIsMissing() ])); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -146,7 +147,7 @@ public async Task ExecuteAsync_Returns400_WhenResourceGroupIsMissing() ])); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -162,7 +163,7 @@ public async Task ExecuteAsync_Returns400_WhenResourceTypeIsMissing() ])); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -190,7 +191,7 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsGenericException() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Service error", response.Message); } @@ -218,7 +219,7 @@ public async Task ExecuteAsync_Returns400_WhenServiceThrowsInvalidOperationExcep var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Resource not found", response.Message); } @@ -246,7 +247,7 @@ public async Task ExecuteAsync_Returns503_WhenServiceIsUnavailable() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(503, response.Status); + Assert.Equal(HttpStatusCode.ServiceUnavailable, response.Status); Assert.Contains("Service Unavailable", response.Message); } @@ -281,7 +282,7 @@ public async Task ExecuteAsync_HandlesEmptyDiagnosticResult() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -324,7 +325,7 @@ public async Task ExecuteAsync_Returns400_WhenRequiredParameterIsEmpty( var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } diff --git a/tools/Azure.Mcp.Tools.Authorization/tests/Azure.Mcp.Tools.Authorization.UnitTests/RoleAssignmentListCommandTests.cs b/tools/Azure.Mcp.Tools.Authorization/tests/Azure.Mcp.Tools.Authorization.UnitTests/RoleAssignmentListCommandTests.cs index 6e778339a0..4d1a5ad8f6 100644 --- a/tools/Azure.Mcp.Tools.Authorization/tests/Azure.Mcp.Tools.Authorization.UnitTests/RoleAssignmentListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Authorization/tests/Azure.Mcp.Tools.Authorization.UnitTests/RoleAssignmentListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -144,7 +145,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.AzureBestPractices/src/Commands/BestPracticesCommand.cs b/tools/Azure.Mcp.Tools.AzureBestPractices/src/Commands/BestPracticesCommand.cs index 6d5acc9c6e..beadf2c558 100644 --- a/tools/Azure.Mcp.Tools.AzureBestPractices/src/Commands/BestPracticesCommand.cs +++ b/tools/Azure.Mcp.Tools.AzureBestPractices/src/Commands/BestPracticesCommand.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine.Parsing; +using System.Net; using System.Reflection; using System.Text; using Azure.Mcp.Core.Commands; @@ -69,7 +70,7 @@ public override Task ExecuteAsync(CommandContext context, Parse { if (string.IsNullOrEmpty(options.Resource) || string.IsNullOrEmpty(options.Action)) { - context.Response.Status = 400; + context.Response.Status = HttpStatusCode.BadRequest; context.Response.Message = "Both resource and action parameters are required."; return Task.FromResult(context.Response); } @@ -77,7 +78,7 @@ public override Task ExecuteAsync(CommandContext context, Parse var resourceFileName = GetResourceFileName(options.Resource, options.Action); var bestPractices = GetBestPracticesText(resourceFileName); - context.Response.Status = 200; + context.Response.Status = HttpStatusCode.OK; context.Response.Results = ResponseResult.Create([bestPractices], AzureBestPracticesJsonContext.Default.ListString); context.Response.Message = string.Empty; @@ -131,7 +132,7 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe if (!result.IsValid && commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = result.ErrorMessage!; } diff --git a/tools/Azure.Mcp.Tools.AzureBestPractices/tests/Azure.Mcp.Tools.AzureBestPractices.UnitTests/BestPracticesCommandTests.cs b/tools/Azure.Mcp.Tools.AzureBestPractices/tests/Azure.Mcp.Tools.AzureBestPractices.UnitTests/BestPracticesCommandTests.cs index 2ffb613b97..1b511dda77 100644 --- a/tools/Azure.Mcp.Tools.AzureBestPractices/tests/Azure.Mcp.Tools.AzureBestPractices.UnitTests/BestPracticesCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AzureBestPractices/tests/Azure.Mcp.Tools.AzureBestPractices.UnitTests/BestPracticesCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.AzureBestPractices.Commands; @@ -39,7 +40,7 @@ public async Task ExecuteAsync_GeneralCodeGeneration_ReturnsAzureBestPractices() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -58,7 +59,7 @@ public async Task ExecuteAsync_GeneralDeployment_ReturnsAzureBestPractices() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -77,7 +78,7 @@ public async Task ExecuteAsync_AzureFunctionsCodeGeneration_ReturnsAzureBestPrac // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -96,7 +97,7 @@ public async Task ExecuteAsync_AzureFunctionsDeployment_ReturnsAzureBestPractice // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -117,7 +118,7 @@ public async Task ExecuteAsync_StaticWebAppAll_ReturnsAzureBestPractices() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -138,7 +139,7 @@ public async Task ExecuteAsync_InvalidResource_ReturnsBadRequest() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Invalid resource", response.Message); } @@ -150,7 +151,7 @@ public async Task ExecuteAsync_StaticWebAppWithInvalidAction_ReturnsBadRequest() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("The 'static-web-app' resource only supports 'all' action", response.Message); } @@ -162,7 +163,7 @@ public async Task ExecuteAsync_InvalidAction_ReturnsBadRequest() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Invalid action", response.Message); } @@ -174,7 +175,7 @@ public async Task ExecuteAsync_GeneralWithAllAction_ReturnsAzureBestPractices() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -196,7 +197,7 @@ public async Task ExecuteAsync_AzureFunctionsWithAllAction_ReturnsAzureBestPract // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -220,7 +221,7 @@ public async Task ExecuteAsync_MissingResource_ReturnsBadRequest() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Both resource and action parameters are required", response.Message); } @@ -232,7 +233,7 @@ public async Task ExecuteAsync_MissingAction_ReturnsBadRequest() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Both resource and action parameters are required", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.AzureIsv/tests/Azure.Mcp.Tools.AzureIsv.UnitTests/Datadog/MonitoredResourcesListCommandTests.cs b/tools/Azure.Mcp.Tools.AzureIsv/tests/Azure.Mcp.Tools.AzureIsv.UnitTests/Datadog/MonitoredResourcesListCommandTests.cs index 727e4d2780..1be207e38d 100644 --- a/tools/Azure.Mcp.Tools.AzureIsv/tests/Azure.Mcp.Tools.AzureIsv.UnitTests/Datadog/MonitoredResourcesListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AzureIsv/tests/Azure.Mcp.Tools.AzureIsv.UnitTests/Datadog/MonitoredResourcesListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.AzureIsv.Commands.Datadog; using Azure.Mcp.Tools.AzureIsv.Services; @@ -87,7 +88,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.AzureManagedLustre/src/Commands/FileSystem/FileSystemSubnetSizeCommand.cs b/tools/Azure.Mcp.Tools.AzureManagedLustre/src/Commands/FileSystem/FileSystemSubnetSizeCommand.cs index 214f4b87bf..f730b91e28 100644 --- a/tools/Azure.Mcp.Tools.AzureManagedLustre/src/Commands/FileSystem/FileSystemSubnetSizeCommand.cs +++ b/tools/Azure.Mcp.Tools.AzureManagedLustre/src/Commands/FileSystem/FileSystemSubnetSizeCommand.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine.Parsing; +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.AzureManagedLustre.Options; @@ -72,7 +73,7 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe if (commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = result.ErrorMessage!; } } diff --git a/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemListCommandTests.cs b/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemListCommandTests.cs index 34d495fd76..a6e2f4ca0c 100644 --- a/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -159,7 +160,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -213,13 +214,13 @@ public async Task ExecuteAsync_HandlesRequestFailedException_NotFound() // Arrange - 404 Not Found _amlfsService.ListFileSystemsAsync( Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) - .ThrowsAsync(new RequestFailedException(404, "not found")); + .ThrowsAsync(new RequestFailedException((int)HttpStatusCode.NotFound, "not found")); var args = _commandDefinition.Parse(["--subscription", _knownSubscriptionId]); var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -235,7 +236,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException_Forbidden() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("forbidden", response.Message, StringComparison.OrdinalIgnoreCase); } } diff --git a/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemSubnetSizeCommandTests.cs b/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemSubnetSizeCommandTests.cs index 0e262b5eed..e7b9af5249 100644 --- a/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemSubnetSizeCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemSubnetSizeCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -92,7 +93,7 @@ public async Task ExecuteAsync_ValidSkus_DoNotThrow(string sku) // Assert Assert.NotNull(response); - Assert.True(response.Status == 200 || response.Results != null); + Assert.True(response.Status == HttpStatusCode.OK || response.Results != null); } [Fact] @@ -109,7 +110,7 @@ public async Task ExecuteAsync_InvalidSku_Returns400() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.True(response.Status >= 400); + Assert.True(response.Status >= HttpStatusCode.BadRequest); Assert.Contains("invalid sku", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -126,7 +127,7 @@ public async Task ExecuteAsync_ServiceThrows_IsHandled() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.True(response.Status >= 500); + Assert.True(response.Status >= HttpStatusCode.InternalServerError); Assert.Contains("boom", response.Message, StringComparison.OrdinalIgnoreCase); } } diff --git a/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/Sku/SkuGetCommandTests.cs b/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/Sku/SkuGetCommandTests.cs index 6f1cf7e972..85f33b0513 100644 --- a/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/Sku/SkuGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/Sku/SkuGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -108,6 +109,6 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var parseResult = _commandDefinition.Parse(args.Split(' ', StringSplitOptions.RemoveEmptyEntries)); var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); } } diff --git a/tools/Azure.Mcp.Tools.AzureTerraformBestPractices/src/Commands/AzureTerraformBestPracticesGetCommand.cs b/tools/Azure.Mcp.Tools.AzureTerraformBestPractices/src/Commands/AzureTerraformBestPracticesGetCommand.cs index de38948367..9b796420b8 100644 --- a/tools/Azure.Mcp.Tools.AzureTerraformBestPractices/src/Commands/AzureTerraformBestPracticesGetCommand.cs +++ b/tools/Azure.Mcp.Tools.AzureTerraformBestPractices/src/Commands/AzureTerraformBestPracticesGetCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Reflection; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Helpers; @@ -42,10 +43,12 @@ private static string LoadBestPracticesText() Secret = false }; + protected override EmptyOptions BindOptions(ParseResult parseResult) => new(); + public override Task ExecuteAsync(CommandContext context, ParseResult parseResult) { var bestPractices = GetBestPracticesText(); - context.Response.Status = 200; + context.Response.Status = HttpStatusCode.OK; context.Response.Results = ResponseResult.Create([bestPractices], AzureTerraformBestPracticesJsonContext.Default.ListString); context.Response.Message = string.Empty; return Task.FromResult(context.Response); diff --git a/tools/Azure.Mcp.Tools.CloudArchitect/src/Commands/Design/DesignCommand.cs b/tools/Azure.Mcp.Tools.CloudArchitect/src/Commands/Design/DesignCommand.cs index b00d3a893f..908f34600d 100644 --- a/tools/Azure.Mcp.Tools.CloudArchitect/src/Commands/Design/DesignCommand.cs +++ b/tools/Azure.Mcp.Tools.CloudArchitect/src/Commands/Design/DesignCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Reflection; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; @@ -156,7 +157,7 @@ public override Task ExecuteAsync(CommandContext context, Parse ResponseObject = responseObject }; - context.Response.Status = 200; + context.Response.Status = HttpStatusCode.OK; context.Response.Results = ResponseResult.Create(result, CloudArchitectJsonContext.Default.CloudArchitectDesignResponse); context.Response.Message = string.Empty; } diff --git a/tools/Azure.Mcp.Tools.CloudArchitect/tests/Azure.Mcp.Tools.CloudArchitect.UnitTests/Design/DesignCommandTests.cs b/tools/Azure.Mcp.Tools.CloudArchitect/tests/Azure.Mcp.Tools.CloudArchitect.UnitTests/Design/DesignCommandTests.cs index 58687fae0c..24049d7f5a 100644 --- a/tools/Azure.Mcp.Tools.CloudArchitect/tests/Azure.Mcp.Tools.CloudArchitect.UnitTests/Design/DesignCommandTests.cs +++ b/tools/Azure.Mcp.Tools.CloudArchitect/tests/Azure.Mcp.Tools.CloudArchitect.UnitTests/Design/DesignCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Reflection; using System.Text; using System.Text.Json; @@ -86,7 +87,7 @@ public async Task ExecuteAsync_ReturnsArchitectureDesignText(string args) var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Empty(response.Message); @@ -129,7 +130,7 @@ public async Task ExecuteAsync_WithAllOptionsSet() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Empty(response.Message); @@ -159,7 +160,7 @@ public async Task ExecuteAsync_HandlesQuotesAndEscapingProperly(string questionW var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Empty(response.Message); @@ -197,7 +198,7 @@ public async Task ExecuteAsync_HandlesComplexEscapingScenarios() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Empty(response.Message); @@ -246,7 +247,7 @@ public async Task ExecuteAsync_LoadsEmbeddedResourceText() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); string serializedResult = SerializeResponseResult(response.Results!); var responseObject = JsonSerializer.Deserialize(serializedResult, CloudArchitectJsonContext.Default.CloudArchitectDesignResponse); @@ -269,7 +270,7 @@ public async Task ExecuteAsync_WithStateOption() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Empty(response.Message); @@ -302,7 +303,7 @@ public async Task ExecuteAsync_WithCompleteOptionSet() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Empty(response.Message); @@ -561,7 +562,7 @@ public async Task ExecuteAsync_WithComplexStateJson_ParsesSuccessfully() // Assert Assert.Empty(parseResult.Errors); - Assert.Equal(200, _context.Response.Status); + Assert.Equal(HttpStatusCode.OK, _context.Response.Status); // Verify that the state was parsed correctly by checking the response string serializedResult = SerializeResponseResult(_context.Response.Results!); @@ -588,7 +589,7 @@ public async Task ExecuteAsync_WithComplexStateJson_ParsesSuccessfully() // var response = await _command.ExecuteAsync(_context, parseResult); // // Assert - The command should handle the error gracefully and return an error response - // Assert.NotEqual(200, response.Status); + // Assert.NotEqual(HttpStatusCode.OK, response.Status); // Assert.NotEmpty(response.Message); // } @@ -603,7 +604,7 @@ public async Task ExecuteAsync_WithEmptyState_CreatesDefaultState() var result = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, _context.Response.Status); + Assert.Equal(HttpStatusCode.OK, _context.Response.Status); string serializedResult = SerializeResponseResult(_context.Response.Results!); var responseObject = JsonSerializer.Deserialize(serializedResult, CloudArchitectJsonContext.Default.CloudArchitectDesignResponse); diff --git a/tools/Azure.Mcp.Tools.Cosmos/src/Commands/BaseCosmosCommand.cs b/tools/Azure.Mcp.Tools.Cosmos/src/Commands/BaseCosmosCommand.cs index 05b5484e0a..2dbf87a8a0 100644 --- a/tools/Azure.Mcp.Tools.Cosmos/src/Commands/BaseCosmosCommand.cs +++ b/tools/Azure.Mcp.Tools.Cosmos/src/Commands/BaseCosmosCommand.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics.CodeAnalysis; +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; using Azure.Mcp.Core.Extensions; @@ -33,9 +34,9 @@ protected override TOptions BindOptions(ParseResult parseResult) _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - CosmosException cosmosEx => (int)cosmosEx.StatusCode, + CosmosException cosmosEx => cosmosEx.StatusCode, _ => base.GetStatusCode(ex) }; } diff --git a/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/AccountListCommandTests.cs b/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/AccountListCommandTests.cs index 75ea734f23..13cd00c701 100644 --- a/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/AccountListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/AccountListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -92,7 +93,7 @@ public async Task ExecuteAsync_Returns400_WhenSubscriptionIsMissing() var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse([])); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -113,7 +114,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -131,7 +132,7 @@ public async Task ExecuteAsync_Returns503_WhenServiceIsUnavailable() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(503, response.Status); + Assert.Equal(HttpStatusCode.ServiceUnavailable, response.Status); Assert.Contains("Service Unavailable", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/ContainerListCommandTests.cs b/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/ContainerListCommandTests.cs index 301ed0384b..8cc07d55d2 100644 --- a/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/ContainerListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/ContainerListCommandTests.cs @@ -1,7 +1,8 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -122,12 +123,12 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() "--subscription", "sub123" ]); - // Act + // Act var response = await _command.ExecuteAsync(_context, args); // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -137,11 +138,11 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() [InlineData("--subscription", "sub123", "--account", "account123")] // Missing database-name public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(params string[] args) { - // Arrange & Act + // Arrange & Act var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse(args)); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/DatabaseListCommandTests.cs b/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/DatabaseListCommandTests.cs index 34aacff23f..8d578418f5 100644 --- a/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/DatabaseListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/DatabaseListCommandTests.cs @@ -1,7 +1,8 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -116,12 +117,12 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() "--subscription", "sub123" ]); - // Act + // Act var response = await _command.ExecuteAsync(_context, args); // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -131,11 +132,11 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() [InlineData("--subscription", "sub123")] public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(params string[] args) { - // Arrange & Act + // Arrange & Act var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse(args)); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/ItemQueryCommandTests.cs b/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/ItemQueryCommandTests.cs index bbab8c8094..a2dca1da48 100644 --- a/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/ItemQueryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Cosmos/tests/Azure.Mcp.Tools.Cosmos.UnitTests/ItemQueryCommandTests.cs @@ -1,4 +1,5 @@ -using System.CommandLine; +using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -179,7 +180,7 @@ public async Task ExecuteAsync_Returns500_WhenServiceThrowsException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -194,7 +195,7 @@ public async Task ExecuteAsync_Returns400_WhenRequiredParametersAreMissing(param var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse(args)); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Architecture/DiagramGenerateCommand.cs b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Architecture/DiagramGenerateCommand.cs index 272cbe8085..759fe9cc60 100644 --- a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Architecture/DiagramGenerateCommand.cs +++ b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Architecture/DiagramGenerateCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.Deploy.Commands.Infrastructure; @@ -82,7 +83,7 @@ public override Task ExecuteAsync(CommandContext context, Parse if (appTopology.Services.Length == 0) { _logger.LogWarning("No services detected in the app topology."); - context.Response.Status = 200; + context.Response.Status = HttpStatusCode.OK; context.Response.Message = "No service detected."; return Task.FromResult(context.Response); } diff --git a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Pipeline/GuidanceGetCommand.cs b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Pipeline/GuidanceGetCommand.cs index 6ab827dfac..7bd58dcadb 100644 --- a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Pipeline/GuidanceGetCommand.cs +++ b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Pipeline/GuidanceGetCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; using Azure.Mcp.Core.Extensions; @@ -68,7 +69,7 @@ public override Task ExecuteAsync(CommandContext context, Parse var result = PipelineGenerationUtil.GeneratePipelineGuidelines(options); context.Response.Message = result; - context.Response.Status = 200; + context.Response.Status = HttpStatusCode.OK; } catch (Exception ex) { diff --git a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Plan/GetCommand.cs b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Plan/GetCommand.cs index 0608d3f39f..124d164c91 100644 --- a/tools/Azure.Mcp.Tools.Deploy/src/Commands/Plan/GetCommand.cs +++ b/tools/Azure.Mcp.Tools.Deploy/src/Commands/Plan/GetCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Security.Cryptography; using System.Text; using Azure.Mcp.Core.Commands; @@ -83,7 +84,7 @@ public override Task ExecuteAsync(CommandContext context, Parse var planTemplate = DeploymentPlanTemplateUtil.GetPlanTemplate(options.ProjectName, options.TargetAppService, options.ProvisioningTool, options.AzdIacOptions); context.Response.Message = planTemplate; - context.Response.Status = 200; + context.Response.Status = HttpStatusCode.OK; } catch (Exception ex) { diff --git a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/App/LogsGetCommandTests.cs b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/App/LogsGetCommandTests.cs index 3072c0cd7b..345d27ebff 100644 --- a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/App/LogsGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/App/LogsGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.Deploy.Commands.App; using Azure.Mcp.Tools.Deploy.Services; @@ -60,7 +61,7 @@ public async Task Should_get_azd_app_logs() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.StartsWith("App logs retrieved:", result.Message); Assert.Contains("Application started", result.Message); @@ -91,7 +92,7 @@ public async Task Should_get_azd_app_logs_with_default_limit() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.StartsWith("App logs retrieved:", result.Message); } @@ -119,7 +120,7 @@ public async Task Should_handle_no_logs_found() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.Equal("No logs found.", result.Message); } @@ -146,7 +147,7 @@ public async Task Should_handle_error_during_log_retrieval() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.StartsWith("Error during retrieval of app logs", result.Message); Assert.Contains("test-env", result.Message); @@ -174,7 +175,7 @@ public async Task Should_handle_service_exception() // assert Assert.NotNull(result); - Assert.NotEqual(200, result.Status); // Should be an error status + Assert.NotEqual(HttpStatusCode.OK, result.Status); // Should be an error status Assert.NotNull(result.Message); Assert.Contains("Failed to connect to Azure", result.Message); } @@ -194,7 +195,7 @@ public async Task Should_validate_required_parameters() // assert Assert.NotNull(result); - Assert.NotEqual(200, result.Status); // Should fail validation + Assert.NotEqual(HttpStatusCode.OK, result.Status); // Should fail validation } diff --git a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Architecture/DiagramGenerateCommandTests.cs b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Architecture/DiagramGenerateCommandTests.cs index c6291204f2..8725f7e4da 100644 --- a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Architecture/DiagramGenerateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Architecture/DiagramGenerateCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.Deploy.Commands.Architecture; @@ -35,7 +36,7 @@ public async Task GenerateArchitectureDiagram_ShouldReturnNoServiceDetected() var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Contains("No service detected", response.Message); } @@ -47,7 +48,7 @@ public async Task GenerateArchitectureDiagram_InvalidJsonInput() var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Invalid JSON format", response.Message); } @@ -128,7 +129,7 @@ public async Task GenerateArchitectureDiagram_ShouldReturnEncryptedDiagramUrl() var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); // Extract the URL from the response message var graphStartPattern = "```mermaid"; var graphStartIndex = response.Message.IndexOf(graphStartPattern); diff --git a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Infrastructure/RulesGetCommandTests.cs b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Infrastructure/RulesGetCommandTests.cs index 4d60fc255b..bc687c1b02 100644 --- a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Infrastructure/RulesGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Infrastructure/RulesGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.Deploy.Commands.Infrastructure; using Microsoft.Extensions.DependencyInjection; @@ -46,7 +47,7 @@ public async Task Should_get_infrastructure_code_rules() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Deployment Tool azd rules", result.Message, StringComparison.OrdinalIgnoreCase); } @@ -66,7 +67,7 @@ public async Task Should_get_infrastructure_rules_for_terraform() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Expected parameters in terraform parameters", result.Message, StringComparison.OrdinalIgnoreCase); } @@ -86,7 +87,7 @@ public async Task Should_get_infrastructure_rules_for_function_app() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Additional requirements for Function Apps", result.Message, StringComparison.OrdinalIgnoreCase); Assert.Contains("Storage Blob Data Owner", result.Message, StringComparison.OrdinalIgnoreCase); @@ -107,7 +108,7 @@ public async Task Should_get_infrastructure_rules_for_container_app() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Additional requirements for Container Apps", result.Message, StringComparison.OrdinalIgnoreCase); Assert.Contains("mcr.microsoft.com/azuredocs/containerapps-helloworld:latest", result.Message, StringComparison.OrdinalIgnoreCase); @@ -128,7 +129,7 @@ public async Task Should_get_infrastructure_rules_for_azcli_deployment_tool() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("If creating AzCli script, the script should stop if any command fails.", result.Message, StringComparison.OrdinalIgnoreCase); } @@ -148,7 +149,7 @@ public async Task Should_include_necessary_tools_in_response() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Tools needed:", result.Message, StringComparison.OrdinalIgnoreCase); Assert.Contains("az cli", result.Message, StringComparison.OrdinalIgnoreCase); @@ -171,7 +172,7 @@ public async Task Should_handle_multiple_resource_types() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Resources: appservice, containerapp, function", result.Message, StringComparison.OrdinalIgnoreCase); Assert.Contains("App Service Rules", result.Message, StringComparison.OrdinalIgnoreCase); diff --git a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Pipeline/GuidanceGetCommandTests.cs b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Pipeline/GuidanceGetCommandTests.cs index fe2819e34d..5ec4a5f1d2 100644 --- a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Pipeline/GuidanceGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Pipeline/GuidanceGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.Deploy.Commands.Pipeline; using Microsoft.Extensions.DependencyInjection; @@ -45,7 +46,7 @@ public async Task Should_generate_pipeline() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Run \"azd pipeline config\" to help the user create a deployment pipeline.", result.Message); } @@ -67,7 +68,7 @@ public async Task Should_generate_pipeline_with_github_details() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Help the user to set up a CI/CD pipeline", result.Message); Assert.Contains("test-org", result.Message); @@ -88,7 +89,7 @@ public async Task Should_generate_pipeline_with_default_azd_pipeline_config() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Help the user to set up a CI/CD pipeline", result.Message); Assert.Contains("Github Actions workflow", result.Message); @@ -108,7 +109,7 @@ public async Task Should_generate_pipeline_with_minimal_github_info() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("Help the user to set up a CI/CD pipeline", result.Message); Assert.Contains("{$organization-of-repo}", result.Message); @@ -131,7 +132,7 @@ public async Task Should_handle_guid_subscription_id() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains($"User is deploying to subscription {guidSubscriptionId}", result.Message); } @@ -150,7 +151,7 @@ public async Task Should_handle_non_guid_subscription_id() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("az account show --query id -o tsv", result.Message); } @@ -169,7 +170,7 @@ public async Task Should_include_service_principal_creation_steps() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("az ad sp create-for-rbac", result.Message); Assert.Contains("federated-credential create", result.Message); diff --git a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Plan/GetCommandTests.cs b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Plan/GetCommandTests.cs index 6a0a36b166..a70c4f79a9 100644 --- a/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Plan/GetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Deploy/tests/Azure.Mcp.Tools.Deploy.UnitTests/Commands/Plan/GetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.Deploy.Commands.Plan; using Microsoft.Extensions.DependencyInjection; @@ -48,7 +49,7 @@ public async Task GetPlan_Should_Return_Expected_Result() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("# Azure Deployment Plan for django Project", result.Message); Assert.Contains("Azure Container Apps", result.Message); @@ -71,7 +72,7 @@ public async Task Should_get_plan_with_default_iac_options() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("# Azure Deployment Plan for myapp Project", result.Message); Assert.Contains("Azure Web App Service", result.Message); @@ -93,7 +94,7 @@ public async Task Should_get_plan_for_kubernetes() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("# Azure Deployment Plan for k8s-app Project", result.Message); Assert.Contains("Azure Kubernetes Service", result.Message); @@ -114,7 +115,7 @@ public async Task Should_get_plan_with_default_target_service() // assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Message); Assert.Contains("# Azure Deployment Plan for default-app Project", result.Message); Assert.Contains("Azure Container Apps", result.Message); // Should default to Container Apps diff --git a/tools/Azure.Mcp.Tools.EventGrid/src/Commands/Subscription/SubscriptionListCommand.cs b/tools/Azure.Mcp.Tools.EventGrid/src/Commands/Subscription/SubscriptionListCommand.cs index 4c2842e663..dad7b50101 100644 --- a/tools/Azure.Mcp.Tools.EventGrid/src/Commands/Subscription/SubscriptionListCommand.cs +++ b/tools/Azure.Mcp.Tools.EventGrid/src/Commands/Subscription/SubscriptionListCommand.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine.Parsing; +using System.Net; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models.Option; using Azure.Mcp.Tools.EventGrid.Options; @@ -19,8 +20,8 @@ public sealed class SubscriptionListCommand(ILogger log public override string Description => """ - List event subscriptions for topics with filtering and endpoint configuration. This tool shows all active - subscriptions including webhook endpoints, event filters, and delivery retry policies. Returns subscription + List event subscriptions for topics with filtering and endpoint configuration. This tool shows all active + subscriptions including webhook endpoints, event filters, and delivery retry policies. Returns subscription details as JSON array. Requires either --topic (bare topic name) OR --subscription. If only --topic is provided the tool searches all accessible subscriptions for a topic with that name. Optional --resource-group/--location may only be used alongside --subscription or --topic. @@ -73,7 +74,7 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe if (commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = result.ErrorMessage; } } @@ -85,7 +86,7 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe if (commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = result.ErrorMessage; } } diff --git a/tools/Azure.Mcp.Tools.EventGrid/tests/Azure.Mcp.Tools.EventGrid.UnitTests/Subscription/SubscriptionListCommandTests.cs b/tools/Azure.Mcp.Tools.EventGrid/tests/Azure.Mcp.Tools.EventGrid.UnitTests/Subscription/SubscriptionListCommandTests.cs index 4d12057308..de3567a292 100644 --- a/tools/Azure.Mcp.Tools.EventGrid/tests/Azure.Mcp.Tools.EventGrid.UnitTests/Subscription/SubscriptionListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.EventGrid/tests/Azure.Mcp.Tools.EventGrid.UnitTests/Subscription/SubscriptionListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -187,7 +188,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -211,7 +212,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS new("subscription1", "Microsoft.EventGrid/eventSubscriptions", "WebHook", "https://example.com/webhook1", "Succeeded", null, null, 30, 1440, "2023-01-01T00:00:00Z", "2023-01-02T00:00:00Z") ]); - // Set up subscription service for cross-subscription search scenario + // Set up subscription service for cross-subscription search scenario _subscriptionService.GetSubscriptions(Arg.Any(), Arg.Any()) .Returns([]); } @@ -222,7 +223,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); diff --git a/tools/Azure.Mcp.Tools.EventGrid/tests/Azure.Mcp.Tools.EventGrid.UnitTests/Topic/TopicListCommandTests.cs b/tools/Azure.Mcp.Tools.EventGrid/tests/Azure.Mcp.Tools.EventGrid.UnitTests/Topic/TopicListCommandTests.cs index 6d98e99abf..e5cbe16947 100644 --- a/tools/Azure.Mcp.Tools.EventGrid/tests/Azure.Mcp.Tools.EventGrid.UnitTests/Topic/TopicListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.EventGrid/tests/Azure.Mcp.Tools.EventGrid.UnitTests/Topic/TopicListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -113,7 +114,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -145,13 +146,13 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message?.ToLower() ?? ""); } } diff --git a/tools/Azure.Mcp.Tools.Extension/src/Commands/AzCommand.cs b/tools/Azure.Mcp.Tools.Extension/src/Commands/AzCommand.cs index 06f8572301..22c7e5be03 100644 --- a/tools/Azure.Mcp.Tools.Extension/src/Commands/AzCommand.cs +++ b/tools/Azure.Mcp.Tools.Extension/src/Commands/AzCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Runtime.InteropServices; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; @@ -182,7 +183,7 @@ public override async Task ExecuteAsync(CommandContext context, if (result.ExitCode != 0) { - context.Response.Status = 500; + context.Response.Status = HttpStatusCode.InternalServerError; context.Response.Message = result.Error; } diff --git a/tools/Azure.Mcp.Tools.Extension/src/Commands/AzdCommand.cs b/tools/Azure.Mcp.Tools.Extension/src/Commands/AzdCommand.cs index a61d2ff50b..e5ed690b7f 100644 --- a/tools/Azure.Mcp.Tools.Extension/src/Commands/AzdCommand.cs +++ b/tools/Azure.Mcp.Tools.Extension/src/Commands/AzdCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Reflection; using System.Runtime.InteropServices; using Azure.Mcp.Core.Commands; @@ -118,7 +119,7 @@ public override async Task ExecuteAsync(CommandContext context, if (options.Learn && string.IsNullOrWhiteSpace(options.Command)) { context.Response.Message = s_bestPracticesText; - context.Response.Status = 200; + context.Response.Status = HttpStatusCode.OK; return context.Response; } @@ -140,7 +141,7 @@ public override async Task ExecuteAsync(CommandContext context, terminalCommand += $" -e {options.Environment}"; } - context.Response.Status = 400; + context.Response.Status = HttpStatusCode.BadRequest; context.Response.Message = $""" The requested command is a long-running command and is better suited to be run in a terminal. @@ -292,7 +293,7 @@ private static CommandResponse HandleSuccess(ProcessResult result, string comman private static CommandResponse HandleError(ProcessResult result, CommandResponse response) { - response.Status = 500; + response.Status = HttpStatusCode.InternalServerError; response.Message = result.Error; var contentResults = new List diff --git a/tools/Azure.Mcp.Tools.Extension/src/Commands/AzqrCommand.cs b/tools/Azure.Mcp.Tools.Extension/src/Commands/AzqrCommand.cs index 13dfcf7bbb..a7a69fbf45 100644 --- a/tools/Azure.Mcp.Tools.Extension/src/Commands/AzqrCommand.cs +++ b/tools/Azure.Mcp.Tools.Extension/src/Commands/AzqrCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Runtime.InteropServices; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; @@ -105,20 +106,20 @@ public override async Task ExecuteAsync(CommandContext context, if (result.ExitCode != 0) { - response.Status = 500; + response.Status = HttpStatusCode.InternalServerError; response.Message = result.Error; return response; } if (!File.Exists(xlsxReportFilePath) && !File.Exists(jsonReportFilePath)) { - response.Status = 500; + response.Status = HttpStatusCode.InternalServerError; response.Message = $"Report file '{xlsxReportFilePath}' and '{jsonReportFilePath}' were not found after azqr execution."; return response; } var resultObj = new AzqrReportResult(xlsxReportFilePath, jsonReportFilePath, result.Output); response.Results = ResponseResult.Create(resultObj, ExtensionJsonContext.Default.AzqrReportResult); - response.Status = 200; + response.Status = HttpStatusCode.OK; response.Message = "azqr report generated successfully."; return response; } diff --git a/tools/Azure.Mcp.Tools.Extension/tests/Azure.Mcp.Tools.Extension.UnitTests/AzqrCommandTests.cs b/tools/Azure.Mcp.Tools.Extension/tests/Azure.Mcp.Tools.Extension.UnitTests/AzqrCommandTests.cs index 26c660bd90..1cdf183a86 100644 --- a/tools/Azure.Mcp.Tools.Extension/tests/Azure.Mcp.Tools.Extension.UnitTests/AzqrCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Extension/tests/Azure.Mcp.Tools.Extension.UnitTests/AzqrCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Reflection; using System.Runtime.InteropServices; using Azure.Mcp.Core.Models.Command; @@ -83,7 +84,7 @@ public async Task ExecuteAsync_ReturnsSuccessResult_WhenScanSucceeds() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("azqr report generated successfully.", response.Message); await _processService.Received().ExecuteAsync( Arg.Any(), @@ -123,6 +124,6 @@ public async Task ExecuteAsync_ReturnsBadRequest_WhenMissingSubscriptionArgument // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } } diff --git a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/DeploymentsListCommandTests.cs b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/DeploymentsListCommandTests.cs index ad00f36f52..c355fb65f9 100644 --- a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/DeploymentsListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/DeploymentsListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.AI.Projects; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -91,7 +92,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -113,7 +114,7 @@ public async Task ExecuteAsync_ReturnsError_WhenMissingEndpoint() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.StartsWith("Missing Required options: --endpoint", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/KnowledgeIndexListCommandTests.cs b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/KnowledgeIndexListCommandTests.cs index de91f36c79..846f56dbcc 100644 --- a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/KnowledgeIndexListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/KnowledgeIndexListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Foundry.Commands; @@ -65,7 +66,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -90,7 +91,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -114,7 +115,7 @@ public async Task ExecuteAsync_ReturnsExpectedResults() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/KnowledgeIndexSchemaCommandTests.cs b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/KnowledgeIndexSchemaCommandTests.cs index ccc69e3d35..aba63007b2 100644 --- a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/KnowledgeIndexSchemaCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/KnowledgeIndexSchemaCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Foundry.Commands; @@ -71,7 +72,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -96,7 +97,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -121,7 +122,7 @@ public async Task ExecuteAsync_ReturnsExpectedResults() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/ModelDeploymentCommandTests.cs b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/ModelDeploymentCommandTests.cs index 8f78679f6c..14667984ef 100644 --- a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/ModelDeploymentCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/ModelDeploymentCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Foundry.Commands; @@ -147,7 +148,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } diff --git a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/ModelsListCommandTests.cs b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/ModelsListCommandTests.cs index c079b853e1..705c6fa3b2 100644 --- a/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/ModelsListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Foundry/tests/Azure.Mcp.Tools.Foundry.UnitTests/ModelsListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -147,7 +148,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.FunctionApp/src/Commands/FunctionApp/FunctionAppGetCommand.cs b/tools/Azure.Mcp.Tools.FunctionApp/src/Commands/FunctionApp/FunctionAppGetCommand.cs index cd642398d1..ed018d2c25 100644 --- a/tools/Azure.Mcp.Tools.FunctionApp/src/Commands/FunctionApp/FunctionAppGetCommand.cs +++ b/tools/Azure.Mcp.Tools.FunctionApp/src/Commands/FunctionApp/FunctionAppGetCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models.Option; @@ -102,9 +103,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "Function App not found. Verify the app name, resource group, and subscription, and ensure you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the Function App. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.FunctionApp/tests/Azure.Mcp.Tools.FunctionApp.UnitTests/FunctionApp/FunctionAppGetCommandTests.cs b/tools/Azure.Mcp.Tools.FunctionApp/tests/Azure.Mcp.Tools.FunctionApp.UnitTests/FunctionApp/FunctionAppGetCommandTests.cs index 783bb09115..738cdfa537 100644 --- a/tools/Azure.Mcp.Tools.FunctionApp/tests/Azure.Mcp.Tools.FunctionApp.UnitTests/FunctionApp/FunctionAppGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.FunctionApp/tests/Azure.Mcp.Tools.FunctionApp.UnitTests/FunctionApp/FunctionAppGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -64,7 +65,7 @@ public async Task ExecuteAsync_Listing_ValidatesInputCorrectly(string args, bool var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -100,7 +101,7 @@ public async Task ExecuteAsync_ReturnsFunctionAppList() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the mock was called @@ -144,7 +145,7 @@ public async Task ExecuteAsync_ReturnsEmptyWhenNoFunctionApp() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -168,7 +169,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -199,7 +200,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); } [Fact] @@ -214,7 +215,7 @@ public async Task ExecuteAsync_ReturnsFunctionApp() var response = await _command.ExecuteAsync(context, parseResult); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -235,7 +236,7 @@ public async Task ExecuteAsync_ReturnsEmptyWhenNotFound() var response = await _command.ExecuteAsync(context, parseResult); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); diff --git a/tools/Azure.Mcp.Tools.Grafana/tests/Azure.Mcp.Tools.Grafana.UnitTests/WorkspaceListCommandTests.cs b/tools/Azure.Mcp.Tools.Grafana/tests/Azure.Mcp.Tools.Grafana.UnitTests/WorkspaceListCommandTests.cs index 4b4e88f120..4ff63bb8cb 100644 --- a/tools/Azure.Mcp.Tools.Grafana/tests/Azure.Mcp.Tools.Grafana.UnitTests/WorkspaceListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Grafana/tests/Azure.Mcp.Tools.Grafana.UnitTests/WorkspaceListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -164,7 +165,7 @@ public async Task ExecuteAsync_HandlesException_AndSetsException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateCreateCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateCreateCommandTests.cs index 75b1e9571e..c3ed534c79 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateCreateCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.KeyVault.Commands.Certificate; @@ -75,7 +76,7 @@ await _keyVaultService.Received(1).CreateCertificate( Arg.Any()); // Should handle the exception - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); } [Fact] @@ -93,7 +94,7 @@ public async Task ExecuteAsync_ReturnsInvalidObject_IfCertificateNameIsEmpty() // Assert - Should return validation error response Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -122,7 +123,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateGetCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateGetCommandTests.cs index 2b730b4fa8..1bead0a540 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.KeyVault.Commands.Certificate; @@ -75,7 +76,7 @@ await _keyVaultService.Received(1).GetCertificate( Arg.Any()); // Should handle the exception - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); } [Fact] @@ -93,7 +94,7 @@ public async Task ExecuteAsync_ReturnsInvalidObject_IfCertificateNameIsEmpty() // Assert - Should return validation error response Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -122,7 +123,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateImportCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateImportCommandTests.cs index 2d16e992a2..54d611f7c3 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateImportCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateImportCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.KeyVault.Commands.Certificate; @@ -75,7 +76,7 @@ await _keyVaultService.Received(1).ImportCertificate( _knownSubscription, Arg.Any(), Arg.Any()); - Assert.Equal(500, response.Status); // due to forced exception + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); // due to forced exception } public static IEnumerable RequiredArgumentCases() @@ -116,7 +117,7 @@ public async Task ExecuteAsync_ValidatesRequiredArguments(string argLine, bool s // Assert if (shouldPassValidation) { - Assert.NotEqual(400, response.Status); // could be 500 due to forced exception, but not a validation failure + Assert.NotEqual(HttpStatusCode.BadRequest, response.Status); // could be 500 due to forced exception, but not a validation failure await _keyVaultService.Received(1).ImportCertificate( _knownVault, _knownCertName, @@ -128,7 +129,7 @@ await _keyVaultService.Received(1).ImportCertificate( } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } } @@ -154,7 +155,7 @@ public async Task ExecuteAsync_HandlesServiceException() var response = await _command.ExecuteAsync(_context, args); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expected, response.Message); } @@ -193,7 +194,7 @@ await _keyVaultService.Received(1).ImportCertificate( _knownSubscription, Arg.Any(), Arg.Any()); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); } [Fact] @@ -229,7 +230,7 @@ await _keyVaultService.Received(1).ImportCertificate( _knownSubscription, Arg.Any(), Arg.Any()); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); } [Fact] @@ -266,7 +267,7 @@ await _keyVaultService.Received(1).ImportCertificate( _knownSubscription, Arg.Any(), Arg.Any()); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); } finally { @@ -303,7 +304,7 @@ public async Task ExecuteAsync_Returns500_OnInvalidCertificateData() var response = await _command.ExecuteAsync(_context, args); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(errorMessage, response.Message); } @@ -334,7 +335,7 @@ public async Task ExecuteAsync_Returns500_OnInvalidPassword() var response = await _command.ExecuteAsync(_context, args); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(mismatchMessage, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateListCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateListCommandTests.cs index 14d9051692..65fc902c4e 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Certificate/CertificateListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -127,7 +128,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyCreateCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyCreateCommandTests.cs index c4a0bd841d..065cf1aebd 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyCreateCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -110,7 +111,7 @@ public async Task ExecuteAsync_ReturnsInvalidObject_IfKeyNameIsEmpty() // Assert - Should return validation error response Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -141,7 +142,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyGetCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyGetCommandTests.cs index 515b85d788..6702aeac8d 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -82,7 +83,7 @@ public async Task ExecuteAsync_ReturnsKey() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -108,7 +109,7 @@ public async Task ExecuteAsync_ReturnsInvalidObject_IfKeyNameIsEmpty() // Assert - Should return validation error response Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -137,7 +138,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyListCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyListCommandTests.cs index 21547b2b09..304582859b 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Key/KeyListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -131,7 +132,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretCreateCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretCreateCommandTests.cs index f438e03178..28ecf73a94 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretCreateCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -73,7 +74,7 @@ public async Task ExecuteAsync_CreatesSecret_WhenValidInput() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -99,7 +100,7 @@ public async Task ExecuteAsync_ReturnsInvalidObject_IfSecretNameIsEmpty() // Assert - Should return validation error response Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -130,7 +131,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretGetCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretGetCommandTests.cs index ebf9d5c782..8d9beaa05c 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -71,7 +72,7 @@ public async Task ExecuteAsync_ReturnsSecret() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -97,7 +98,7 @@ public async Task ExecuteAsync_ReturnsInvalidObject_IfSecretNameIsEmpty() // Assert - Should return validation error response Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -126,7 +127,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretListCommandTests.cs b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretListCommandTests.cs index 7b16666d24..b9f992a617 100644 --- a/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.UnitTests/Secret/SecretListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -127,7 +128,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Kusto/src/Commands/BaseClusterCommand.cs b/tools/Azure.Mcp.Tools.Kusto/src/Commands/BaseClusterCommand.cs index ff877e4bea..d0db6e9bbf 100644 --- a/tools/Azure.Mcp.Tools.Kusto/src/Commands/BaseClusterCommand.cs +++ b/tools/Azure.Mcp.Tools.Kusto/src/Commands/BaseClusterCommand.cs @@ -3,6 +3,7 @@ using System.CommandLine.Parsing; using System.Diagnostics.CodeAnalysis; +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; using Azure.Mcp.Core.Extensions; @@ -47,7 +48,7 @@ public override ValidationResult Validate(CommandResult parseResult, CommandResp if (commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = validationResult.ErrorMessage; } } diff --git a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/ClusterGetCommandTests.cs b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/ClusterGetCommandTests.cs index ee1aef56a5..4e35a5e95e 100644 --- a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/ClusterGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/ClusterGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -87,7 +88,7 @@ public async Task ExecuteAsync_ReturnsNull_WhenClusterDoesNotExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Null(response.Results); } @@ -106,7 +107,7 @@ public async Task ExecuteAsync_HandlesException_AndSetsException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/ClusterListCommandTests.cs b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/ClusterListCommandTests.cs index 40fcd889e9..2708b7bdc1 100644 --- a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/ClusterListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/ClusterListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -103,7 +104,7 @@ public async Task ExecuteAsync_HandlesException_AndSetsException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/DatabaseListCommandTests.cs b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/DatabaseListCommandTests.cs index 401a9618e7..588281507a 100644 --- a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/DatabaseListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/DatabaseListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -135,7 +136,7 @@ public async Task ExecuteAsync_HandlesException_AndSetsException(string cliArgs, // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -150,7 +151,7 @@ public async Task ExecuteAsync_ReturnsBadRequest_WhenMissingRequiredOptions() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Either --cluster-uri must be provided", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -165,7 +166,7 @@ public async Task ExecuteAsync_ReturnsBadRequest_WhenMissingAllRequiredOptions() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Either --cluster-uri must be provided", response.Message, StringComparison.OrdinalIgnoreCase); } } diff --git a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/QueryCommandTests.cs b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/QueryCommandTests.cs index 264e063bcf..c0b0303791 100644 --- a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/QueryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/QueryCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -142,7 +143,7 @@ public async Task ExecuteAsync_HandlesException_AndSetsException(string cliArgs, var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -157,7 +158,7 @@ public async Task ExecuteAsync_ReturnsBadRequest_WhenMissingRequiredOptions() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Either --cluster-uri must be provided", response.Message, StringComparison.OrdinalIgnoreCase); } } diff --git a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/SampleCommandTests.cs b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/SampleCommandTests.cs index 7b5099c015..c0b621e718 100644 --- a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/SampleCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/SampleCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -141,7 +142,7 @@ public async Task ExecuteAsync_ReturnsEmpty_WhenNoResults(string cliArgs, bool u // var response = await command.ExecuteAsync(context, args); // Assert.NotNull(response); - // Assert.Equal(500, response.Status); + // Assert.Equal(HttpStatusCode.InternalServerError, response.Status); // Assert.Equal(expectedError, response.Message); // } @@ -155,6 +156,6 @@ public async Task ExecuteAsync_ReturnsBadRequest_WhenMissingRequiredOptions() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } } diff --git a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/TableListCommandTests.cs b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/TableListCommandTests.cs index 45a14f012f..d754c7fbe9 100644 --- a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/TableListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/TableListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -134,7 +135,7 @@ public async Task ExecuteAsync_HandlesException_AndSetsException(string cliArgs, var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -148,6 +149,6 @@ public async Task ExecuteAsync_ReturnsBadRequest_WhenMissingRequiredOptions() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } } diff --git a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/TableSchemaCommandTests.cs b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/TableSchemaCommandTests.cs index 3a4b7382ac..f73aa40c7a 100644 --- a/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/TableSchemaCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/TableSchemaCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -105,7 +106,7 @@ public async Task ExecuteAsync_ReturnsNull_WhenNoSchema(string cliArgs, bool use // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -137,7 +138,7 @@ public async Task ExecuteAsync_HandlesException_AndSetsException(string cliArgs, var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -151,6 +152,6 @@ public async Task ExecuteAsync_ReturnsBadRequest_WhenMissingRequiredOptions() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } } diff --git a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestCreateCommandTests.cs b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestCreateCommandTests.cs index 0cab342c7e..792766cc90 100644 --- a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestCreateCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -75,7 +76,7 @@ public async Task ExecuteAsync_CreateLoadTest_WhenExists() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, LoadTestJsonContext.Default.TestCreateCommandResult); @@ -105,7 +106,7 @@ public async Task ExecuteAsync_HandlesBadRequestErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } [Fact] @@ -134,7 +135,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestGetCommandTests.cs b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestGetCommandTests.cs index 7651bdb376..74459411c9 100644 --- a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -63,7 +64,7 @@ public async Task ExecuteAsync_ReturnsLoadTest_WhenExists() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, LoadTestJsonContext.Default.TestGetCommandResult); @@ -91,7 +92,7 @@ public async Task ExecuteAsync_HandlesBadRequestErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } [Fact] @@ -111,7 +112,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestResourceCreateTests.cs b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestResourceCreateTests.cs index 92cdb56306..ef0eb8ff6d 100644 --- a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestResourceCreateTests.cs +++ b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestResourceCreateTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -111,7 +112,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() "--tenant", "tenant123" ]); var response = await _command.ExecuteAsync(context, parseResult); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestResourcesListCommandTests.cs b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestResourcesListCommandTests.cs index 6232e841d9..923adbed5a 100644 --- a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestResourcesListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestResourcesListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -137,7 +138,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() "--tenant", "tenant123" ]); var response = await _command.ExecuteAsync(context, parseResult); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunCreateCommandTests.cs b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunCreateCommandTests.cs index be28386cd5..51baf01a57 100644 --- a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunCreateCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -57,7 +58,7 @@ public async Task ExecuteAsync_CreatesLoadTestRun() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, LoadTestJsonContext.Default.TestRunCreateCommandResult); @@ -82,7 +83,7 @@ public async Task ExecuteAsync_RerunLoadTestRun() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, LoadTestJsonContext.Default.TestRunCreateCommandResult); @@ -105,7 +106,7 @@ public async Task ExecuteAsync_HandlesBadRequestErrors() var args = command.GetCommand().Parse("--subscription sub123 --resource-group resourceGroup123 --test-resource-name testResourceName --tenant tenant123 --testrun-id run1"); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } [Fact] @@ -119,7 +120,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var args = command.GetCommand().Parse("--subscription sub123 --resource-group resourceGroup123 --test-resource-name testResourceName --testrun-id run1 --tenant tenant123 --test-id testId1"); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunGetCommandTests.cs b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunGetCommandTests.cs index df06f133e6..0fa3bc9faf 100644 --- a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -63,7 +64,7 @@ public async Task ExecuteAsync_ReturnsLoadTestRun_WhenExists() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, LoadTestJsonContext.Default.TestRunGetCommandResult); @@ -88,7 +89,7 @@ public async Task ExecuteAsync_HandlesBadRequestErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } [Fact] @@ -109,7 +110,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunListCommandTests.cs b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunListCommandTests.cs index 662bd51c1a..8f0fd370b8 100644 --- a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -67,7 +68,7 @@ public async Task ExecuteAsync_ReturnsLoadTestRuns_WhenExists() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, LoadTestJsonContext.Default.TestRunListCommandResult); @@ -96,7 +97,7 @@ public async Task ExecuteAsync_HandlesBadRequestErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } [Fact] @@ -116,7 +117,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunUpdateCommandTests.cs b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunUpdateCommandTests.cs index f877acb3df..5cba4288e4 100644 --- a/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunUpdateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.LoadTesting/tests/Azure.Mcp.Tools.LoadTesting.UnitTests/TestRunUpdateCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.LoadTesting.Commands.LoadTestRun; @@ -62,7 +63,7 @@ public async Task ExecuteAsync_UpdateLoadTestRun_TestNotExisting() var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); } [Fact] @@ -83,6 +84,6 @@ public async Task ExecuteAsync_HandlesBadRequestErrors() ]); var context = new CommandContext(_serviceProvider); var response = await command.ExecuteAsync(context, args); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } } diff --git a/tools/Azure.Mcp.Tools.Marketplace/src/Commands/Product/ProductGetCommand.cs b/tools/Azure.Mcp.Tools.Marketplace/src/Commands/Product/ProductGetCommand.cs index 3bc5d88e33..fdf79dbd7b 100644 --- a/tools/Azure.Mcp.Tools.Marketplace/src/Commands/Product/ProductGetCommand.cs +++ b/tools/Azure.Mcp.Tools.Marketplace/src/Commands/Product/ProductGetCommand.cs @@ -126,10 +126,10 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - HttpRequestException httpEx => (int)httpEx.StatusCode.GetValueOrDefault(HttpStatusCode.InternalServerError), - ArgumentException => 400, + HttpRequestException httpEx => httpEx.StatusCode.GetValueOrDefault(HttpStatusCode.InternalServerError), + ArgumentException => HttpStatusCode.BadRequest, _ => base.GetStatusCode(ex) }; diff --git a/tools/Azure.Mcp.Tools.Marketplace/src/Commands/Product/ProductListCommand.cs b/tools/Azure.Mcp.Tools.Marketplace/src/Commands/Product/ProductListCommand.cs index 75b6e9e276..b2a98460e8 100644 --- a/tools/Azure.Mcp.Tools.Marketplace/src/Commands/Product/ProductListCommand.cs +++ b/tools/Azure.Mcp.Tools.Marketplace/src/Commands/Product/ProductListCommand.cs @@ -140,10 +140,10 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - HttpRequestException httpEx => (int)httpEx.StatusCode.GetValueOrDefault(HttpStatusCode.InternalServerError), - ArgumentException => 400, + HttpRequestException httpEx => httpEx.StatusCode.GetValueOrDefault(HttpStatusCode.InternalServerError), + ArgumentException => HttpStatusCode.BadRequest, _ => base.GetStatusCode(ex) }; diff --git a/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.LiveTests/ProductListCommandTests.cs b/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.LiveTests/ProductListCommandTests.cs index 5907b97ef2..0e600350ba 100644 --- a/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.LiveTests/ProductListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.LiveTests/ProductListCommandTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Text.Json; diff --git a/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.UnitTests/Product/ProductGetCommandTests.cs b/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.UnitTests/Product/ProductGetCommandTests.cs index acd9e71b02..9d7cf5f8dc 100644 --- a/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.UnitTests/Product/ProductGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.UnitTests/Product/ProductGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Marketplace.Commands.Product; @@ -82,7 +83,7 @@ public async Task ExecuteAsync_WithValidParameters_ReturnsSuccess() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -97,7 +98,7 @@ public async Task ExecuteAsync_WithMissingSubscription_ReturnsValidationError() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -131,7 +132,7 @@ public async Task ExecuteAsync_HandlesServiceException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.UnitTests/Product/ProductListCommandTests.cs b/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.UnitTests/Product/ProductListCommandTests.cs index c9d6bc17ef..c5ba076673 100644 --- a/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.UnitTests/Product/ProductListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Marketplace/tests/Azure.Mcp.Tools.Marketplace.UnitTests/Product/ProductListCommandTests.cs @@ -1,6 +1,7 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -84,7 +85,7 @@ public async Task ExecuteAsync_WithValidParameters_ReturnsSuccess() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -128,7 +129,7 @@ public async Task ExecuteAsync_WithOptionalParameters_ReturnsSuccess() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -143,7 +144,7 @@ public async Task ExecuteAsync_WithMissingSubscription_ReturnsValidationError() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } @@ -173,7 +174,7 @@ public async Task ExecuteAsync_WithEmptyResults_ReturnsSuccessWithNullResults() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -204,7 +205,7 @@ public async Task ExecuteAsync_HandlesServiceException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -250,7 +251,7 @@ public async Task ExecuteAsync_WithMultipleODataOptions_ReturnsSuccess() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -300,7 +301,7 @@ public async Task ExecuteAsync_WithResultsContainingNextCursor_ReturnsNextCursor // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the response contains the NextCursor by checking the serialized result @@ -350,7 +351,7 @@ public async Task ExecuteAsync_WithoutNextCursorInResults_DoesNotIncludeNextCurs // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the response contains null NextCursor @@ -398,7 +399,7 @@ public async Task ExecuteAsync_WithExpandOption_ReturnsSuccess() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } } diff --git a/tools/Azure.Mcp.Tools.Monitor/src/Commands/HealthModels/Entity/EntityGetHealthCommand.cs b/tools/Azure.Mcp.Tools.Monitor/src/Commands/HealthModels/Entity/EntityGetHealthCommand.cs index 451aa80bf4..7fac4916b5 100644 --- a/tools/Azure.Mcp.Tools.Monitor/src/Commands/HealthModels/Entity/EntityGetHealthCommand.cs +++ b/tools/Azure.Mcp.Tools.Monitor/src/Commands/HealthModels/Entity/EntityGetHealthCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Monitor.Options; using Azure.Mcp.Tools.Monitor.Services; @@ -85,10 +86,10 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - KeyNotFoundException => 404, - ArgumentException => 400, + KeyNotFoundException => HttpStatusCode.NotFound, + ArgumentException => HttpStatusCode.BadRequest, _ => base.GetStatusCode(ex) }; } diff --git a/tools/Azure.Mcp.Tools.Monitor/src/Commands/Metrics/MetricsQueryCommand.cs b/tools/Azure.Mcp.Tools.Monitor/src/Commands/Metrics/MetricsQueryCommand.cs index 2e07e09a59..7c4a05ec38 100644 --- a/tools/Azure.Mcp.Tools.Monitor/src/Commands/Metrics/MetricsQueryCommand.cs +++ b/tools/Azure.Mcp.Tools.Monitor/src/Commands/Metrics/MetricsQueryCommand.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine.Parsing; +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Monitor.Models; using Azure.Mcp.Tools.Monitor.Options; @@ -81,7 +82,7 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe if (commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = result.ErrorMessage!; } } @@ -97,7 +98,7 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe if (commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = result.ErrorMessage!; } } @@ -165,7 +166,7 @@ public override async Task ExecuteAsync(CommandContext context, $"increase the interval size (e.g., use PT1H instead of PT5M), " + $"or increase the --max-buckets parameter."; - context.Response.Status = 400; + context.Response.Status = HttpStatusCode.BadRequest; context.Response.Message = errorMessage; _logger.LogWarning("Bucket limit exceeded. Metric: {MetricName}, BucketCount: {BucketCount}, MaxBuckets: {MaxBuckets}", diff --git a/tools/Azure.Mcp.Tools.Monitor/src/Services/MonitorHealthModelService.cs b/tools/Azure.Mcp.Tools.Monitor/src/Services/MonitorHealthModelService.cs index a73d5940c7..ada815c2e2 100644 --- a/tools/Azure.Mcp.Tools.Monitor/src/Services/MonitorHealthModelService.cs +++ b/tools/Azure.Mcp.Tools.Monitor/src/Services/MonitorHealthModelService.cs @@ -146,7 +146,7 @@ private async Task GetCachedTokenAsync( private async Task GetEntraIdAccessTokenAsync(string resource) { - var tokenRequestContext = new TokenRequestContext(new[] { $"{resource}/.default" }); + var tokenRequestContext = new TokenRequestContext([$"{resource}/.default"]); var tokenCredential = await GetCredential(); return await tokenCredential .GetTokenAsync(tokenRequestContext, CancellationToken.None) diff --git a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/HealthModels/Entity/EntityGetHealthCommandTests.cs b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/HealthModels/Entity/EntityGetHealthCommandTests.cs index c3d11c5a0e..f5c23c833d 100644 --- a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/HealthModels/Entity/EntityGetHealthCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/HealthModels/Entity/EntityGetHealthCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json.Nodes; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -74,7 +75,7 @@ public async Task ExecuteAsync_WithValidParameters_ReturnsEntityHealth() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); await _monitorHealthService.Received(1).GetEntityHealth( @@ -97,7 +98,7 @@ public async Task ExecuteAsync_WithMissingRequiredParameters_ReturnsBadRequest() // Assert Assert.NotNull(result); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Contains("required", result.Message, StringComparison.OrdinalIgnoreCase); // Verify service was not called @@ -133,7 +134,7 @@ public async Task ExecuteAsync_EntityNotFound_ReturnsNotFound() // Assert Assert.NotNull(result); - Assert.Equal(404, result.Status); + Assert.Equal(HttpStatusCode.NotFound, result.Status); Assert.Contains("not found", result.Message, StringComparison.OrdinalIgnoreCase); await _monitorHealthService.Received(1).GetEntityHealth( @@ -168,7 +169,7 @@ public async Task ExecuteAsync_InvalidArgument_ReturnsBadRequest() // Assert Assert.NotNull(result); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Contains("Invalid argument", result.Message); await _monitorHealthService.Received(1).GetEntityHealth( @@ -204,7 +205,7 @@ public async Task ExecuteAsync_GeneralException_ReturnsInternalServerError() // Assert Assert.NotNull(result); - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.Contains(expectedError, result.Message); await _monitorHealthService.Received(1).GetEntityHealth( @@ -242,7 +243,7 @@ public async Task ExecuteAsync_WithAuthMethod_PassesAuthMethodToService() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); await _monitorHealthService.Received(1).GetEntityHealth( TestEntity, @@ -280,7 +281,7 @@ public async Task ExecuteAsync_WithRetryPolicy_PassesRetryPolicyToService() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); await _monitorHealthService.Received(1).GetEntityHealth( TestEntity, diff --git a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Log/ResourceLogQueryCommandTests.cs b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Log/ResourceLogQueryCommandTests.cs index b6f9bc9399..c0ae192b2e 100644 --- a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Log/ResourceLogQueryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Log/ResourceLogQueryCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json.Nodes; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -78,7 +79,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse(args)); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -117,7 +118,7 @@ public async Task ExecuteAsync_ReturnsQueryResults() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the mock was called @@ -154,7 +155,7 @@ public async Task ExecuteAsync_CallsServiceWithCorrectParameters() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _monitorService.Received(1).QueryResourceLogs( _knownSubscription, _knownResourceId, @@ -188,7 +189,7 @@ public async Task ExecuteAsync_WithDefaultParameters_UsesExpectedDefaults() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _monitorService.Received(1).QueryResourceLogs( _knownSubscription, _knownResourceId, @@ -221,7 +222,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -251,7 +252,7 @@ public async Task ExecuteAsync_WithComplexResourceId_HandlesCorrectly() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _monitorService.Received(1).QueryResourceLogs( _knownSubscription, complexResourceId, diff --git a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Log/WorkspaceLogQueryCommandTests.cs b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Log/WorkspaceLogQueryCommandTests.cs index 149640f290..eb3adc02b5 100644 --- a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Log/WorkspaceLogQueryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Log/WorkspaceLogQueryCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json.Nodes; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -79,7 +80,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse(args)); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -118,7 +119,7 @@ public async Task ExecuteAsync_ReturnsQueryResults() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the mock was called @@ -155,7 +156,7 @@ public async Task ExecuteAsync_CallsServiceWithCorrectParameters() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _monitorService.Received(1).QueryWorkspaceLogs( _knownSubscription, _knownWorkspace, @@ -189,7 +190,7 @@ public async Task ExecuteAsync_WithDefaultParameters_UsesExpectedDefaults() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _monitorService.Received(1).QueryWorkspaceLogs( _knownSubscription, _knownWorkspace, @@ -222,7 +223,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Metrics/MetricsDefinitionsCommandTests.cs b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Metrics/MetricsDefinitionsCommandTests.cs index 587d5f0fc1..91e1b70513 100644 --- a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Metrics/MetricsDefinitionsCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Metrics/MetricsDefinitionsCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Monitor.Commands.Metrics; @@ -119,7 +120,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -171,7 +172,7 @@ public async Task ExecuteAsync_CallsServiceWithCorrectParameters() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("All 1 metric definitions returned.", response.Message); await _service.Received(1).ListMetricDefinitionsAsync( @@ -216,7 +217,7 @@ public async Task ExecuteAsync_WithSearchString_CallsServiceWithSearchParameter( var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the service was called with the search string @@ -265,7 +266,7 @@ public async Task ExecuteAsync_WithAllOptionalParameters_CallsServiceCorrectly() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("All 1 metric definitions returned.", response.Message); await _service.Received(1).ListMetricDefinitionsAsync( @@ -305,7 +306,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -333,7 +334,7 @@ public async Task ExecuteAsync_HandlesServiceException_LogsError() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); _logger.Received(1).Log( LogLevel.Error, Arg.Any(), @@ -392,7 +393,7 @@ public async Task ExecuteAsync_WithResults_ReturnsCorrectStructure() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("All 2 metric definitions returned.", response.Message); } @@ -419,7 +420,7 @@ public async Task ExecuteAsync_WithNoResults_ReturnsNullResults() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Null(response.Results); } @@ -445,7 +446,7 @@ public async Task ExecuteAsync_WithNullResults_ReturnsNullResults() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Null(response.Results); } @@ -477,7 +478,7 @@ public async Task ExecuteAsync_WithDefaultLimit_TruncatesResultsTo10() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify that results were truncated - message should indicate truncation Assert.Contains("Results truncated to 10 of 15", response.Message); @@ -510,7 +511,7 @@ public async Task ExecuteAsync_WithCustomLimit_TruncatesResultsCorrectly() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify that results were truncated to the custom limit Assert.Contains("Results truncated to 5 of 20", response.Message); @@ -543,7 +544,7 @@ public async Task ExecuteAsync_WithResultsExceedingLimit_ShowsTruncationMessage( var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify that the message indicates truncation with correct counts Assert.Contains("Results truncated to 8 of 25", response.Message); @@ -576,7 +577,7 @@ public async Task ExecuteAsync_WithResultsUnderLimit_ShowsAllResultsMessage() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify that all results are returned without truncation Assert.Equal("All 3 metric definitions returned.", response.Message); @@ -622,7 +623,7 @@ public async Task ExecuteAsync_BindsOptionsCorrectly() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("All 1 metric definitions returned.", response.Message); await _service.Received(1).ListMetricDefinitionsAsync( diff --git a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Metrics/MetricsQueryCommandTests.cs b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Metrics/MetricsQueryCommandTests.cs index 162e044076..2372fbacda 100644 --- a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Metrics/MetricsQueryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Metrics/MetricsQueryCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -221,12 +222,12 @@ public async Task Validate_MetricNames_ValidatesCorrectly(string metricNames, bo { Assert.NotNull(result.Message); Assert.Contains("Invalid format for --metric-names", result.Message); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); } else { Assert.Equal("Success", result.Message); - Assert.Equal(200, result.Status); // Default status should remain unchanged for valid cases + Assert.Equal(HttpStatusCode.OK, result.Status); // Default status should remain unchanged for valid cases } } @@ -285,7 +286,7 @@ public async Task ExecuteAsync_ValidInput_ReturnsSuccess(string args) var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); @@ -326,7 +327,7 @@ public async Task ExecuteAsync_EmptyResults_ReturnsSuccessWithEmptyResults() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var results = GetResult(response.Results); @@ -397,7 +398,7 @@ public async Task ExecuteAsync_InvalidInput_ReturnsBadRequest(string args) var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.NotEmpty(response.Message); Assert.Null(response.Results); } @@ -453,7 +454,7 @@ public async Task ExecuteAsync_ExceedsBucketLimit_ReturnsBadRequest() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("exceeds the maximum allowed limit of 50", response.Message); Assert.Contains("CPU", response.Message); Assert.Contains("51 time buckets", response.Message); @@ -507,7 +508,7 @@ public async Task ExecuteAsync_ExceedsCustomBucketLimit_ReturnsBadRequest() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("exceeds the maximum allowed limit of 25", response.Message); Assert.Contains("Memory", response.Message); Assert.Contains("26 time buckets", response.Message); @@ -584,7 +585,7 @@ public async Task ExecuteAsync_ChecksAllBucketTypes_ForLimitExceeded(string buck var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("exceeds the maximum allowed limit", response.Message); } @@ -635,7 +636,7 @@ public async Task ExecuteAsync_WithinBucketLimit_ReturnsSuccess() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -726,7 +727,7 @@ public async Task ExecuteAsync_ServiceThrowsException_ReturnsInternalServerError var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Service unavailable", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -826,7 +827,7 @@ public async Task ExecuteAsync_MultipleMetricsWithMixedBucketCounts_ValidatesEac var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Memory", response.Message); Assert.Contains("51 time buckets", response.Message); } @@ -878,7 +879,7 @@ public async Task ExecuteAsync_MultipleTimeSeriesPerMetric_ValidatesAll() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("CPU", response.Message); Assert.Contains("51 time buckets", response.Message); } @@ -930,7 +931,7 @@ public async Task ExecuteAsync_NullBuckets_DoesNotCountTowardsLimit() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -961,7 +962,7 @@ public async Task ExecuteAsync_NullResults_ReturnsSuccessWithEmptyResults() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } diff --git a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Table/TableListCommandTests.cs b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Table/TableListCommandTests.cs index ebe371aeab..2ef87a2b87 100644 --- a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Table/TableListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Table/TableListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -74,7 +75,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse(args)); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -116,7 +117,7 @@ public async Task ExecuteAsync_ReturnsTablesList() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the mock was called @@ -164,7 +165,7 @@ public async Task ExecuteAsync_WithTableTypeParameter_CallsServiceWithCorrectPar var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _monitorService.Received(1).ListTables( _knownSubscription, _knownResourceGroupName, @@ -197,7 +198,7 @@ public async Task ExecuteAsync_ReturnsEmptyWhenNoTables() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -230,7 +231,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/TableType/TableTypeListCommandTests.cs b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/TableType/TableTypeListCommandTests.cs index 18f86cb02b..2d25260be5 100644 --- a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/TableType/TableTypeListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/TableType/TableTypeListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -71,7 +72,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse(args)); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -112,7 +113,7 @@ public async Task ExecuteAsync_ReturnsTableTypesList() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the mock was called @@ -157,7 +158,7 @@ public async Task ExecuteAsync_CallsServiceWithCorrectParameters() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _monitorService.Received(1).ListTableTypes( _knownSubscription, _knownResourceGroup, @@ -188,7 +189,7 @@ public async Task ExecuteAsync_ReturnsEmptyWhenNoTableTypes() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -220,7 +221,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Workspace/WorkspaceListCommandTests.cs b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Workspace/WorkspaceListCommandTests.cs index d0401f2268..c3a6474795 100644 --- a/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Workspace/WorkspaceListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests/Workspace/WorkspaceListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -65,7 +66,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse(args)); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -94,7 +95,7 @@ public async Task ExecuteAsync_ReturnsWorkspacesList() var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse($"--subscription {_knownSubscription}")); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the mock was called @@ -122,7 +123,7 @@ public async Task ExecuteAsync_ReturnsEmptyWhenNoWorkspaces() var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse($"--subscription {_knownSubscription}")); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -143,7 +144,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, _commandDefinition.Parse($"--subscription {_knownSubscription}")); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } diff --git a/tools/Azure.Mcp.Tools.MySql/src/Services/MySqlService.cs b/tools/Azure.Mcp.Tools.MySql/src/Services/MySqlService.cs index e810e12d29..1f7105a5e7 100644 --- a/tools/Azure.Mcp.Tools.MySql/src/Services/MySqlService.cs +++ b/tools/Azure.Mcp.Tools.MySql/src/Services/MySqlService.cs @@ -77,7 +77,7 @@ private async Task GetEntraIdAccessTokenAsync() } } - var tokenRequestContext = new TokenRequestContext(new[] { "https://ossrdbms-aad.database.windows.net/.default" }); + var tokenRequestContext = new TokenRequestContext(["https://ossrdbms-aad.database.windows.net/.default"]); var tokenCredential = await GetCredential(); var accessToken = await tokenCredential .GetTokenAsync(tokenRequestContext, CancellationToken.None) diff --git a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Database/DatabaseListCommandTests.cs b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Database/DatabaseListCommandTests.cs index 577cde79c2..23de2b0342 100644 --- a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Database/DatabaseListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Database/DatabaseListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.MySql.Commands; @@ -49,7 +50,7 @@ public async Task ExecuteAsync_ReturnsDatabases_WhenSuccessful() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -76,7 +77,7 @@ public async Task ExecuteAsync_ReturnsEmpty_WhenNoDatabasesExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -103,7 +104,7 @@ public async Task ExecuteAsync_ReturnsError_WhenServiceThrows() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Access denied", response.Message); } diff --git a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Database/DatabaseQueryCommandTests.cs b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Database/DatabaseQueryCommandTests.cs index d4680bb694..b7d1321531 100644 --- a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Database/DatabaseQueryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Database/DatabaseQueryCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.MySql.Commands; @@ -51,7 +52,7 @@ public async Task ExecuteAsync_ReturnsResults_WhenQuerySucceeds() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -79,7 +80,7 @@ public async Task ExecuteAsync_ReturnsError_WhenQueryFails() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Syntax error", response.Message); } diff --git a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerConfigGetCommandTests.cs b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerConfigGetCommandTests.cs index c8510efc01..f674087720 100644 --- a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerConfigGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerConfigGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.MySql.Commands; @@ -59,7 +60,7 @@ public async Task ExecuteAsync_ReturnsConfiguration_WhenSuccessful() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -87,7 +88,7 @@ public async Task ExecuteAsync_ReturnsError_WhenServiceThrows() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Access denied", response.Message); } diff --git a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerListCommandTests.cs b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerListCommandTests.cs index 2eb2773cd5..01baa3b6b1 100644 --- a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.MySql.Commands; @@ -48,7 +49,7 @@ public async Task ExecuteAsync_ReturnsServers_WhenSuccessful() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -75,7 +76,7 @@ public async Task ExecuteAsync_ReturnsError_WhenServiceThrows() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Access denied", response.Message); } diff --git a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerParamGetCommandTests.cs b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerParamGetCommandTests.cs index 88e7fc80b6..bc521a0b3e 100644 --- a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerParamGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerParamGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.MySql.Commands; @@ -50,7 +51,7 @@ public async Task ExecuteAsync_ReturnsParameter_WhenSuccessful() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -80,7 +81,7 @@ public async Task ExecuteAsync_ReturnsError_WhenServiceThrows() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Parameter 'invalid_param' not found", response.Message); } diff --git a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerParamSetCommandTests.cs b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerParamSetCommandTests.cs index 6b34d5f469..e37158ef14 100644 --- a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerParamSetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Server/ServerParamSetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.MySql.Commands; @@ -51,7 +52,7 @@ public async Task ExecuteAsync_SetsParameter_WhenSuccessful() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -82,7 +83,7 @@ public async Task ExecuteAsync_ReturnsError_WhenServiceThrows() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Parameter 'invalid_param' not found", response.Message); } diff --git a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Table/TableListCommandTests.cs b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Table/TableListCommandTests.cs index 621f497fa8..923753309b 100644 --- a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Table/TableListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Table/TableListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.MySql.Commands; @@ -50,7 +51,7 @@ public async Task ExecuteAsync_ReturnsTables_WhenSuccessful() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -79,7 +80,7 @@ public async Task ExecuteAsync_ReturnsError_WhenServiceThrows() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Access denied", response.Message); } diff --git a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Table/TableSchemaGetCommandTests.cs b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Table/TableSchemaGetCommandTests.cs index bd2c66d881..ebcc17c788 100644 --- a/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Table/TableSchemaGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.MySql/tests/Azure.Mcp.Tools.MySql.UnitTests/Table/TableSchemaGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.MySql.Commands; @@ -51,7 +52,7 @@ public async Task ExecuteAsync_ReturnsSchema_WhenSuccessful() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -79,7 +80,7 @@ public async Task ExecuteAsync_ReturnsError_WhenTableNotFound() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Table not found", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Postgres/src/Services/PostgresService.cs b/tools/Azure.Mcp.Tools.Postgres/src/Services/PostgresService.cs index 03730b0f8d..36fa045671 100644 --- a/tools/Azure.Mcp.Tools.Postgres/src/Services/PostgresService.cs +++ b/tools/Azure.Mcp.Tools.Postgres/src/Services/PostgresService.cs @@ -27,7 +27,7 @@ private async Task GetEntraIdAccessTokenAsync() return _cachedEntraIdAccessToken; } - var tokenRequestContext = new TokenRequestContext(new[] { "https://ossrdbms-aad.database.windows.net/.default" }); + var tokenRequestContext = new TokenRequestContext(["https://ossrdbms-aad.database.windows.net/.default"]); var tokenCredential = await GetCredential(); var accessToken = await tokenCredential .GetTokenAsync(tokenRequestContext, CancellationToken.None) diff --git a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Database/DatabaseListCommandTests.cs b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Database/DatabaseListCommandTests.cs index a668e70e07..b1e8d0f967 100644 --- a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Database/DatabaseListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Database/DatabaseListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.TestUtilities; @@ -45,7 +46,7 @@ public async Task ExecuteAsync_ReturnsDatabases_WhenDatabasesExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -67,7 +68,7 @@ public async Task ExecuteAsync_ReturnsMessage_WhenNoDatabasesExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -88,7 +89,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test exception", response.Message); } @@ -111,7 +112,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Database/DatabaseQueryCommandTests.cs b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Database/DatabaseQueryCommandTests.cs index ffcfb8d0b6..ccf5978976 100644 --- a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Database/DatabaseQueryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Database/DatabaseQueryCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.TestUtilities; @@ -49,7 +50,7 @@ public async Task ExecuteAsync_ReturnsQueryResults_WhenQueryIsValid() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -71,7 +72,7 @@ public async Task ExecuteAsync_ReturnsEmpty_WhenQueryFails() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -103,7 +104,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerConfigGetCommandTests.cs b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerConfigGetCommandTests.cs index 7ee9acb131..62b6dbeaa4 100644 --- a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerConfigGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerConfigGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.TestUtilities; @@ -44,7 +45,7 @@ public async Task ExecuteAsync_ReturnsConfig_WhenConfigExists() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -64,7 +65,7 @@ public async Task ExecuteAsync_ReturnsNull_WhenConfigDoesNotExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.Null(response.Results); } @@ -88,7 +89,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerListCommandTests.cs b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerListCommandTests.cs index aa24e6dbcc..31183bd1f9 100644 --- a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.TestUtilities; @@ -43,7 +44,7 @@ public async Task ExecuteAsync_ReturnsServers_WhenServersExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -88,7 +89,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -109,7 +110,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerParamGetCommandTests.cs b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerParamGetCommandTests.cs index 32d0f29cfa..ea88818eff 100644 --- a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerParamGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerParamGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.TestUtilities; @@ -43,7 +44,7 @@ public async Task ExecuteAsync_ReturnsParamValue_WhenParamExists() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -64,7 +65,7 @@ public async Task ExecuteAsync_ReturnsNull_WhenParamDoesNotExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.Null(response.Results); } @@ -90,7 +91,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerParamSetCommandTests.cs b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerParamSetCommandTests.cs index 54fccb2ef5..003623a6e6 100644 --- a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerParamSetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Server/ServerParamSetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.TestUtilities; @@ -43,7 +44,7 @@ public async Task ExecuteAsync_ReturnsSuccessMessage_WhenParamIsSet() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -66,7 +67,7 @@ public async Task ExecuteAsync_ReturnsNull_WhenParamDoesNotExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.Null(response.Results); } @@ -94,7 +95,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Table/TableListCommandTests.cs b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Table/TableListCommandTests.cs index b917c951c8..08935f691f 100644 --- a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Table/TableListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Table/TableListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.TestUtilities; @@ -43,7 +44,7 @@ public async Task ExecuteAsync_ReturnsTables_WhenTablesExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -64,7 +65,7 @@ public async Task ExecuteAsync_ReturnsEmptyList_WhenNoTablesExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -95,7 +96,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Table/TableSchemaGetCommandTests.cs b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Table/TableSchemaGetCommandTests.cs index 1c05009e0f..0af79cb6f8 100644 --- a/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Table/TableSchemaGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Postgres/tests/Azure.Mcp.Tools.Postgres.UnitTests/Table/TableSchemaGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.TestUtilities; @@ -43,7 +44,7 @@ public async Task ExecuteAsync_ReturnsSchema_WhenSchemaExists() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, PostgresJsonContext.Default.TableSchemaGetCommandResult); @@ -63,7 +64,7 @@ public async Task ExecuteAsync_ReturnsEmpty_WhenSchemaDoesNotExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -95,7 +96,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Quota/tests/Azure.Mcp.Tools.Quota.UnitTests/Commands/Region/AvailabilityListCommandTests.cs b/tools/Azure.Mcp.Tools.Quota/tests/Azure.Mcp.Tools.Quota.UnitTests/Commands/Region/AvailabilityListCommandTests.cs index 3bd09da98a..d6e77f36f1 100644 --- a/tools/Azure.Mcp.Tools.Quota/tests/Azure.Mcp.Tools.Quota.UnitTests/Commands/Region/AvailabilityListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Quota/tests/Azure.Mcp.Tools.Quota.UnitTests/Commands/Region/AvailabilityListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.Quota.Commands; @@ -76,7 +77,7 @@ public async Task Should_check_azure_regions_success() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Verify the service was called with the correct parameters @@ -146,7 +147,7 @@ public async Task Should_check_regions_with_cognitive_services_success() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Verify the service was called with the correct parameters @@ -192,7 +193,7 @@ public async Task Should_ReturnError_empty_resource_types() // Assert Assert.NotNull(result); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Contains("Missing Required options: --resource-types", result.Message); // Verify the service was not called @@ -232,7 +233,7 @@ public async Task Should_handle_service_exception() // Assert Assert.NotNull(result); - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.Contains("Service error occurred", result.Message); } @@ -269,7 +270,7 @@ public async Task Should_parse_multiple_resource_types_with_spaces() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); // Verify the service was called with correctly parsed resource types await _quotaService.Received(1).GetAvailableRegionsForResourceTypesAsync( @@ -311,7 +312,7 @@ public async Task Should_return_empty_results_when_no_regions_found() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Should be empty when no regions are found var json = JsonSerializer.Serialize(result.Results); @@ -356,7 +357,7 @@ public async Task Should_include_all_cognitive_service_parameters() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); // Verify the service was called with all cognitive service parameters await _quotaService.Received(1).GetAvailableRegionsForResourceTypesAsync( @@ -388,7 +389,7 @@ public async Task Should_handle_whitespace_only_resource_types() // Assert Assert.NotNull(result); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Contains("Missing Required options: --resource-types", result.Message); // Verify the service was not called @@ -433,7 +434,7 @@ public async Task Should_handle_mixed_casing_in_resource_types() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); // Verify the service was called with resource types preserving original casing await _quotaService.Received(1).GetAvailableRegionsForResourceTypesAsync( @@ -491,7 +492,7 @@ public async Task Should_handle_very_long_resource_types_list() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Verify the service was called with all 50 resource types diff --git a/tools/Azure.Mcp.Tools.Quota/tests/Azure.Mcp.Tools.Quota.UnitTests/Commands/Usage/CheckCommandTests.cs b/tools/Azure.Mcp.Tools.Quota/tests/Azure.Mcp.Tools.Quota.UnitTests/Commands/Usage/CheckCommandTests.cs index 8d7ad4ecb2..71d0c19021 100644 --- a/tools/Azure.Mcp.Tools.Quota/tests/Azure.Mcp.Tools.Quota.UnitTests/Commands/Usage/CheckCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Quota/tests/Azure.Mcp.Tools.Quota.UnitTests/Commands/Usage/CheckCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.Quota.Commands; @@ -88,7 +89,7 @@ public async Task Should_check_azure_quota_success() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Verify the service was called with the correct parameters @@ -150,7 +151,7 @@ public async Task Should_ReturnError_empty_resource_types() // Assert Assert.NotNull(result); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); } [Fact] @@ -181,7 +182,7 @@ public async Task Should_handle_service_exception() // Assert Assert.NotNull(result); - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.Contains("Service error occurred", result.Message); } @@ -223,7 +224,7 @@ public async Task Should_parse_resource_types_with_spaces() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); // Verify the service was called with correctly parsed resource types await _quotaService.Received(1).GetAzureQuotaAsync( @@ -263,7 +264,7 @@ public async Task Should_return_empty_results_when_no_quotas_found() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Should be empty when no quotas are found var json = JsonSerializer.Serialize(result.Results); @@ -294,7 +295,7 @@ public async Task Should_handle_whitespace_only_resource_types() // Assert Assert.NotNull(result); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); } [Fact] @@ -335,7 +336,7 @@ public async Task Should_handle_mixed_casing_resource_types() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Verify the service was called with the correct casing preserved @@ -389,7 +390,7 @@ public async Task Should_handle_unsupported_provider_returns_no_limit() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Verify the service was called with the correct parameters @@ -458,7 +459,7 @@ public async Task Should_handle_very_long_resource_types_list() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Verify the service was called with all 50 resource types @@ -516,7 +517,7 @@ public async Task Should_handle_network_failure_returns_descriptive_usage_info() // Assert Assert.NotNull(result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); // Verify the service was called with the correct parameters diff --git a/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/CacheForRedis/AccessPolicyListCommandTests.cs b/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/CacheForRedis/AccessPolicyListCommandTests.cs index 8e1bebce3e..f6fa7c7be0 100644 --- a/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/CacheForRedis/AccessPolicyListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/CacheForRedis/AccessPolicyListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -58,7 +59,7 @@ public async Task ExecuteAsync_ReturnsAccessPolicyAssignments_WhenAssignmentsExi var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -120,7 +121,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -147,7 +148,7 @@ public async Task ExecuteAsync_ReturnsError_WhenRequiredParameterIsMissing(strin var response = await command.ExecuteAsync(context, parseResult); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message, StringComparison.OrdinalIgnoreCase); } } diff --git a/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/CacheForRedis/CacheListCommandTests.cs b/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/CacheForRedis/CacheListCommandTests.cs index 287dc6860b..164e91719c 100644 --- a/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/CacheForRedis/CacheListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/CacheForRedis/CacheListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -47,7 +48,7 @@ public async Task ExecuteAsync_ReturnsCaches_WhenCachesExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -95,7 +96,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -117,7 +118,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/ManagedRedis/ClusterListCommandTests.cs b/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/ManagedRedis/ClusterListCommandTests.cs index de929b53c8..beae075d11 100644 --- a/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/ManagedRedis/ClusterListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/ManagedRedis/ClusterListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -47,7 +48,7 @@ public async Task ExecuteAsync_ReturnsClusters_WhenClustersExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -94,7 +95,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -116,7 +117,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/ManagedRedis/DatabaseListCommandTests.cs b/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/ManagedRedis/DatabaseListCommandTests.cs index 3b4f6719ff..efe27c8300 100644 --- a/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/ManagedRedis/DatabaseListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Redis/tests/Azure.Mcp.Tools.Redis.UnitTests/ManagedRedis/DatabaseListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models; using Azure.Mcp.Core.Models.Command; @@ -74,7 +75,7 @@ public async Task ExecuteAsync_ReturnsDatabases_WhenDatabasesExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -137,7 +138,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -164,7 +165,7 @@ public async Task ExecuteAsync_ReturnsError_WhenRequiredParameterIsMissing(strin var response = await command.ExecuteAsync(context, parseResult); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message, StringComparison.OrdinalIgnoreCase); } } diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/src/Services/ResourceHealthService.cs b/tools/Azure.Mcp.Tools.ResourceHealth/src/Services/ResourceHealthService.cs index 9233fb4b27..93cfd94c73 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/src/Services/ResourceHealthService.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/src/Services/ResourceHealthService.cs @@ -32,7 +32,7 @@ public async Task GetAvailabilityStatusAsync( { var credential = await GetCredential(); var token = await credential.GetTokenAsync( - new TokenRequestContext(new[] { $"{AzureManagementBaseUrl}/.default" }), + new TokenRequestContext([$"{AzureManagementBaseUrl}/.default"]), CancellationToken.None); using var client = _httpClientService.CreateClient(new Uri(AzureManagementBaseUrl)); @@ -78,7 +78,7 @@ public async Task> ListAvailabilityStatusesAsync( var credential = await GetCredential(); var token = await credential.GetTokenAsync( - new TokenRequestContext(new[] { $"{AzureManagementBaseUrl}/.default" }), + new TokenRequestContext([$"{AzureManagementBaseUrl}/.default"]), CancellationToken.None); using var client = _httpClientService.CreateClient(new Uri(AzureManagementBaseUrl)); @@ -131,7 +131,7 @@ public async Task> ListServiceHealthEventsAsync( var credential = await GetCredential(); var token = await credential.GetTokenAsync( - new TokenRequestContext(new[] { $"{AzureManagementBaseUrl}/.default" }), + new TokenRequestContext([$"{AzureManagementBaseUrl}/.default"]), CancellationToken.None); using var client = _httpClientService.CreateClient(new Uri(AzureManagementBaseUrl)); diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/AvailabilityStatus/AvailabilityStatusGetCommandTests.cs b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/AvailabilityStatus/AvailabilityStatusGetCommandTests.cs index 7b40949db8..a791a98dc1 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/AvailabilityStatus/AvailabilityStatusGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/AvailabilityStatus/AvailabilityStatusGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -54,7 +55,7 @@ public async Task ExecuteAsync_ReturnsAvailabilityStatus_WhenResourceExists() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -85,7 +86,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -114,7 +115,7 @@ public async Task ExecuteAsync_ReturnsError_WhenParameterIsMissing(string missin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/AvailabilityStatus/AvailabilityStatusListCommandTests.cs b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/AvailabilityStatus/AvailabilityStatusListCommandTests.cs index cae91d0711..51d766f402 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/AvailabilityStatus/AvailabilityStatusListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/AvailabilityStatus/AvailabilityStatusListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -63,7 +64,7 @@ public async Task ExecuteAsync_ReturnsAvailabilityStatuses_WhenResourcesExist() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -101,7 +102,7 @@ public async Task ExecuteAsync_ReturnsFilteredAvailabilityStatuses_WhenResourceG var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -130,7 +131,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Equal(expectedError, response.Message); } @@ -152,7 +153,7 @@ public async Task ExecuteAsync_ReturnsError_WhenRequiredParameterIsMissing(strin var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Equal($"Missing Required options: {missingParameter}", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/ServiceHealthEvents/ServiceHealthEventsListCommandTests.cs b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/ServiceHealthEvents/ServiceHealthEventsListCommandTests.cs index a570850649..b8b5e75b26 100644 --- a/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/ServiceHealthEvents/ServiceHealthEventsListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ResourceHealth/tests/Azure.Mcp.Tools.ResourceHealth.UnitTests/ServiceHealthEvents/ServiceHealthEventsListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.ResourceHealth.Commands.ServiceHealthEvents; @@ -81,13 +82,13 @@ public async Task ExecuteAsync_ValidatesInput(string args, bool shouldSucceed) // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response); Assert.Equal("Success", response.Message); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); // Error message might contain "required" for missing subscription or "Invalid" for enum validation Assert.True( response.Message?.ToLower().Contains("required") == true || @@ -120,7 +121,7 @@ public async Task ExecuteAsync_WithValidSubscription_ReturnsSuccess() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); } @@ -148,7 +149,7 @@ public async Task ExecuteAsync_HandlesServiceError() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains(expectedError, response.Message ?? ""); } diff --git a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexGetCommandTests.cs b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexGetCommandTests.cs index 0b57fd7bb7..15d7fba131 100644 --- a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexGetCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -107,7 +108,7 @@ public async Task ExecuteAsync_HandlesException() var response = await command.ExecuteAsync(context, args); Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -134,7 +135,7 @@ public async Task ExecuteAsync_ReturnsIndexDefinition_WhenIndexExists() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, SearchJsonContext.Default.IndexGetCommandResult); @@ -197,7 +198,7 @@ public async Task ExecuteAsync_HandlesServiceException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains(expectedError, response.Message ?? string.Empty); } @@ -215,7 +216,7 @@ public async Task ExecuteAsync_ValidatesRequiredOptions() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.NotNull(response.Message); Assert.Contains("service", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexQueryCommandTests.cs b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexQueryCommandTests.cs index bc77e261d5..f9074028b5 100644 --- a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexQueryCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Index/IndexQueryCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -68,7 +69,7 @@ public async Task ExecuteAsync_ReturnsResults_WhenSearchSucceeds() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -98,7 +99,7 @@ public async Task ExecuteAsync_HandlesServiceException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains(expectedError, response.Message ?? string.Empty); } @@ -116,7 +117,7 @@ public async Task ExecuteAsync_ValidatesRequiredOptions() // Assert Assert.NotNull(response); - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.NotNull(response.Message); Assert.Contains("service", response.Message); Assert.Contains("index", response.Message); diff --git a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Service/ServiceListCommandTests.cs b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Service/ServiceListCommandTests.cs index d981dcb3bb..72ff0a3283 100644 --- a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Service/ServiceListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.UnitTests/Service/ServiceListCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -105,7 +106,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } } diff --git a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Queue/QueueDetailsCommand.cs b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Queue/QueueDetailsCommand.cs index e0904ec4c9..e63ba17698 100644 --- a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Queue/QueueDetailsCommand.cs +++ b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Queue/QueueDetailsCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; using Azure.Mcp.Core.Extensions; @@ -92,9 +93,9 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => 404, + ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => HttpStatusCode.NotFound, _ => base.GetStatusCode(ex) }; internal record QueueDetailsCommandResult(QueueDetails QueueDetails); diff --git a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Queue/QueuePeekCommand.cs b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Queue/QueuePeekCommand.cs index 48894d85c5..f356f06194 100644 --- a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Queue/QueuePeekCommand.cs +++ b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Queue/QueuePeekCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; using Azure.Mcp.Core.Extensions; @@ -97,9 +98,9 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => 404, + ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => HttpStatusCode.NotFound, _ => base.GetStatusCode(ex) }; diff --git a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/SubscriptionDetailsCommand.cs b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/SubscriptionDetailsCommand.cs index 99f4cfd068..1be6db611e 100644 --- a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/SubscriptionDetailsCommand.cs +++ b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/SubscriptionDetailsCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; using Azure.Mcp.Core.Extensions; @@ -95,9 +96,9 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => 404, + ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => HttpStatusCode.NotFound, _ => base.GetStatusCode(ex) }; diff --git a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/SubscriptionPeekCommand.cs b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/SubscriptionPeekCommand.cs index 95d74f732b..1fa96da8ac 100644 --- a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/SubscriptionPeekCommand.cs +++ b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/SubscriptionPeekCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; using Azure.Mcp.Core.Extensions; @@ -102,9 +103,9 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => 404, + ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => HttpStatusCode.NotFound, _ => base.GetStatusCode(ex) }; diff --git a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/TopicDetailsCommand.cs b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/TopicDetailsCommand.cs index 16085deb8d..c27ae325ab 100644 --- a/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/TopicDetailsCommand.cs +++ b/tools/Azure.Mcp.Tools.ServiceBus/src/Commands/Topic/TopicDetailsCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; using Azure.Mcp.Core.Extensions; @@ -92,9 +93,9 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => 404, + ServiceBusException sbEx when sbEx.Reason == ServiceBusFailureReason.MessagingEntityNotFound => HttpStatusCode.NotFound, _ => base.GetStatusCode(ex) }; diff --git a/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Queue/QueueDetailsCommandTests.cs b/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Queue/QueueDetailsCommandTests.cs index 0d9655fbcd..4df3bf54da 100644 --- a/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Queue/QueueDetailsCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Queue/QueueDetailsCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -107,7 +108,7 @@ public async Task ExecuteAsync_HandlesQueueNotFound() // Assert Assert.NotNull(response); - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("Queue not found", response.Message); } @@ -131,7 +132,7 @@ public async Task ExecuteAsync_HandlesGenericException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -169,12 +170,12 @@ public async Task ExecuteAsync_ValidatesRequiredParameters(string args, bool sho // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Topic/SubscriptionDetailsCommandTests.cs b/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Topic/SubscriptionDetailsCommandTests.cs index e5e00f7aea..2dbce1caad 100644 --- a/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Topic/SubscriptionDetailsCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Topic/SubscriptionDetailsCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -120,7 +121,7 @@ public async Task ExecuteAsync_HandlesSubscriptionNotFound() // Assert Assert.NotNull(response); - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message); } @@ -150,7 +151,7 @@ public async Task ExecuteAsync_HandlesGenericException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -190,12 +191,12 @@ public async Task ExecuteAsync_ValidatesRequiredParameters(string args, bool sho // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Topic/TopicDetailsCommandTests.cs b/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Topic/TopicDetailsCommandTests.cs index 9be61d11a7..6e0bf7f64a 100644 --- a/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Topic/TopicDetailsCommandTests.cs +++ b/tools/Azure.Mcp.Tools.ServiceBus/tests/Azure.Mcp.Tools.ServiceBus.UnitTests/Topic/TopicDetailsCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -107,7 +108,7 @@ public async Task ExecuteAsync_HandlesTopicNotFound() // Assert Assert.NotNull(response); - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); } [Fact] @@ -130,7 +131,7 @@ public async Task ExecuteAsync_HandlesGenericException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -168,12 +169,12 @@ public async Task ExecuteAsync_ValidatesRequiredParameters(string args, bool sho // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } 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 dfeabd0890..0cc16e0137 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseCreateCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.Sql.Models; @@ -107,13 +108,13 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 409 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Conflict => "Database already exists with the specified name. Choose a different database name or use the update command.", - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server not found. Verify the server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed creating the SQL database. Verify you have appropriate permissions. Details: {reqEx.Message}", - RequestFailedException reqEx when reqEx.Status == 400 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.BadRequest => $"Invalid database configuration. Check your SKU, size, and other parameters. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) 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 f1a730f3dd..eded77fda8 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseDeleteCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseDeleteCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Sql.Options.Database; using Azure.Mcp.Tools.Sql.Services; @@ -70,9 +71,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server or database not found. Verify the server name, database name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed deleting the SQL database. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseListCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseListCommand.cs index f28f000ed1..0c917d1e7b 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseListCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseListCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.Database; @@ -72,9 +73,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server not found. Verify the server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the SQL server. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseShowCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseShowCommand.cs index e35491446a..cd525df405 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseShowCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseShowCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.Database; @@ -76,18 +77,18 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { KeyNotFoundException => $"SQL database not found. Verify the database name, server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "Database or server not found. Verify the database name, server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the SQL database. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - KeyNotFoundException => 404, - RequestFailedException reqEx => reqEx.Status, + KeyNotFoundException => HttpStatusCode.NotFound, + RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, _ => base.GetStatusCode(ex) }; 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 e54e92153c..f30de5fccc 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseUpdateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Database/DatabaseUpdateCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.Sql.Models; @@ -107,11 +108,11 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL database or server not found. Verify the database name, server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed updating the SQL database. Verify you have appropriate permissions. Details: {reqEx.Message}", - RequestFailedException reqEx when reqEx.Status == 400 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.BadRequest => $"Invalid database configuration. Check your update parameters. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) 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 7000ccde90..bcde4e1035 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/ElasticPool/ElasticPoolListCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/ElasticPool/ElasticPoolListCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.ElasticPool; @@ -72,9 +73,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server not found. Verify the server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the SQL server. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) 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 1edacb6395..50412b5e86 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/EntraAdmin/EntraAdminListCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/EntraAdmin/EntraAdminListCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.EntraAdmin; @@ -69,9 +70,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server not found. Verify the server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the SQL server. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) 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 43654ced68..59fa34bf5d 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleCreateCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.Sql.Models; @@ -92,9 +93,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server not found. Verify the server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed creating the firewall rule. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx when reqEx.Status == 409 => "A firewall rule with this name already exists. Choose a different name or update the existing rule.", @@ -103,10 +104,10 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - RequestFailedException reqEx => reqEx.Status, - ArgumentException => 400, + RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, + ArgumentException => HttpStatusCode.BadRequest, _ => base.GetStatusCode(ex) }; 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 03569f2e24..706a2c4749 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleDeleteCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleDeleteCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.Sql.Options; @@ -85,19 +86,19 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server or firewall rule not found. Verify the server name, rule name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed deleting the firewall rule. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, ArgumentException argEx => argEx.Message, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - RequestFailedException reqEx => reqEx.Status, - ArgumentException => 400, + RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, + ArgumentException => HttpStatusCode.BadRequest, _ => base.GetStatusCode(ex) }; 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 692552911e..c0fb047f4a 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleListCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleListCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.FirewallRule; @@ -69,9 +70,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server not found. Verify the server name, resource group, and that you have access.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed accessing the SQL server. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) 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 7c723fe1fc..41b87ed3fe 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerCreateCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.Sql.Models; @@ -98,21 +99,21 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 409 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Conflict => "A SQL server with this name already exists. Choose a different server name.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed creating the SQL server. Verify you have appropriate permissions. Details: {reqEx.Message}", - RequestFailedException reqEx when reqEx.Status == 400 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.BadRequest => $"Invalid request parameters for SQL server creation: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, ArgumentException argEx => $"Invalid parameter: {argEx.Message}", _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - RequestFailedException reqEx => reqEx.Status, - ArgumentException => 400, + RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, + ArgumentException => HttpStatusCode.BadRequest, _ => base.GetStatusCode(ex) }; 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 76905fe25b..ef5279bd72 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerDeleteCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerDeleteCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.Sql.Options; @@ -63,7 +64,7 @@ public override async Task ExecuteAsync(CommandContext context, // Show warning about destructive operation unless force is specified if (!options.Force) { - context.Response.Status = 200; + context.Response.Status = HttpStatusCode.OK; context.Response.Message = $"WARNING: This operation will permanently delete the SQL server '{options.Server}' " + $"and ALL its databases in resource group '{options.ResourceGroup}'. " + @@ -85,7 +86,7 @@ public override async Task ExecuteAsync(CommandContext context, } else { - context.Response.Status = 404; + context.Response.Status = HttpStatusCode.NotFound; context.Response.Message = $"SQL server '{options.Server}' not found in resource group '{options.ResourceGroup}'."; } } @@ -102,9 +103,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => $"The given SQL server not found. It may have already been deleted.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed deleting the SQL server. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx when reqEx.Status == 409 => $"Cannot delete SQL server due to a conflict. It may be in use or have dependent resources. Details: {reqEx.Message}", @@ -113,10 +114,10 @@ public override async Task ExecuteAsync(CommandContext context, _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - RequestFailedException reqEx => reqEx.Status, - ArgumentException => 400, + RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, + ArgumentException => HttpStatusCode.BadRequest, _ => base.GetStatusCode(ex) }; diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerShowCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerShowCommand.cs index dab455e1df..e3200b7d34 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerShowCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerShowCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.Sql.Models; using Azure.Mcp.Tools.Sql.Options.Server; @@ -71,20 +72,20 @@ public override async Task ExecuteAsync(CommandContext context, { KeyNotFoundException => "SQL server not found in the specified resource group. Verify the server name and resource group.", - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "SQL server not found in the specified resource group. Verify the server name and resource group.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed retrieving the SQL server. Verify you have appropriate permissions. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, ArgumentException argEx => $"Invalid parameter: {argEx.Message}", _ => base.GetErrorMessage(ex) }; - protected override int GetStatusCode(Exception ex) => ex switch + protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch { - KeyNotFoundException => 404, - RequestFailedException reqEx => reqEx.Status, - ArgumentException => 400, + KeyNotFoundException => HttpStatusCode.NotFound, + RequestFailedException reqEx => (HttpStatusCode)reqEx.Status, + ArgumentException => HttpStatusCode.BadRequest, _ => base.GetStatusCode(ex) }; diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs index 38b21a239f..72625d31e0 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Options; using Azure.Mcp.Core.Services.Azure; @@ -150,7 +151,7 @@ public async Task CreateDatabaseAsync( // Configure read scale if provided if (!string.IsNullOrEmpty(readScale)) { - if (Enum.TryParse(readScale, true, out var readScaleEnum)) + if (Enum.TryParse(readScale, true, out var readScaleEnum)) { databaseData.ReadScale = readScaleEnum; } @@ -261,7 +262,7 @@ public async Task UpdateDatabaseAsync( } if (!string.IsNullOrEmpty(readScale) && - Enum.TryParse(readScale, true, out var readScaleEnum)) + Enum.TryParse(readScale, true, out var readScaleEnum)) { databaseData.ReadScale = readScaleEnum; } @@ -542,7 +543,7 @@ public async Task DeleteFirewallRuleAsync( return true; } - catch (RequestFailedException ex) when (ex.Status == 404) + catch (RequestFailedException ex) when (ex.Status == (int)HttpStatusCode.NotFound) { _logger.LogWarning( "Firewall rule not found during delete operation. Server: {Server}, ResourceGroup: {ResourceGroup}, Rule: {Rule}", @@ -687,7 +688,7 @@ public async Task GetServerAsync( Tags: tags ); } - catch (RequestFailedException reqEx) when (reqEx.Status == 404) + catch (RequestFailedException reqEx) when (reqEx.Status == (int)HttpStatusCode.NotFound) { throw new KeyNotFoundException($"SQL server '{serverName}' not found in resource group '{resourceGroup}' for subscription '{subscription}'."); } @@ -778,7 +779,7 @@ public async Task DeleteServerAsync( return true; } - catch (RequestFailedException reqEx) when (reqEx.Status == 404) + catch (RequestFailedException reqEx) when (reqEx.Status == (int)HttpStatusCode.NotFound) { _logger.LogWarning( "SQL server not found during delete operation. Server: {Server}, ResourceGroup: {ResourceGroup}, Subscription: {Subscription}", @@ -833,7 +834,7 @@ public async Task DeleteDatabaseAsync( return true; } - catch (RequestFailedException ex) when (ex.Status == 404) + catch (RequestFailedException ex) when (ex.Status == (int)HttpStatusCode.NotFound) { _logger.LogWarning( "Database not found during delete operation. Server: {Server}, Database: {Database}, ResourceGroup: {ResourceGroup}", diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.LiveTests/SqlCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.LiveTests/SqlCommandTests.cs index e9da19a881..b102ce9f12 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.LiveTests/SqlCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.LiveTests/SqlCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Tests; using Azure.Mcp.Tests.Client; @@ -170,7 +171,7 @@ public async Task Should_DeleteDatabase_Return404ForNonExistentDatabase() catch (Exception ex) { // Some implementations might return 404 - this is also acceptable - Assert.Contains("404", ex.Message); + Assert.Contains(((int)HttpStatusCode.NotFound).ToString(), ex.Message); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseCreateCommandTests.cs index f60eb292df..db55347510 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseCreateCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.Database; @@ -94,7 +95,7 @@ public async Task ExecuteAsync_WithValidParameters_CreatesDatabase() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -157,7 +158,7 @@ public async Task ExecuteAsync_WithOptionalParameters_CreatesDatabase() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -189,7 +190,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -198,7 +199,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_HandlesDatabaseAlreadyExists() { // Arrange - var conflictException = new RequestFailedException(409, "Database already exists"); + var conflictException = new RequestFailedException((int)HttpStatusCode.Conflict, "Database already exists"); _sqlService.CreateDatabaseAsync( Arg.Any(), Arg.Any(), @@ -222,7 +223,7 @@ public async Task ExecuteAsync_HandlesDatabaseAlreadyExists() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(409, response.Status); + Assert.Equal(HttpStatusCode.Conflict, response.Status); Assert.Contains("already exists", response.Message); } @@ -230,7 +231,7 @@ public async Task ExecuteAsync_HandlesDatabaseAlreadyExists() public async Task ExecuteAsync_HandlesServerNotFound() { // Arrange - var notFoundException = new RequestFailedException(404, "Server not found"); + var notFoundException = new RequestFailedException((int)HttpStatusCode.NotFound, "Server not found"); _sqlService.CreateDatabaseAsync( Arg.Any(), Arg.Any(), @@ -254,7 +255,7 @@ public async Task ExecuteAsync_HandlesServerNotFound() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("SQL server not found", response.Message); } @@ -286,7 +287,7 @@ public async Task ExecuteAsync_HandlesAuthorizationFailure() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } @@ -294,7 +295,7 @@ public async Task ExecuteAsync_HandlesAuthorizationFailure() public async Task ExecuteAsync_HandlesInvalidConfiguration() { // Arrange - var badRequestException = new RequestFailedException(400, "Invalid configuration"); + var badRequestException = new RequestFailedException((int)HttpStatusCode.BadRequest, "Invalid configuration"); _sqlService.CreateDatabaseAsync( Arg.Any(), Arg.Any(), @@ -318,7 +319,7 @@ public async Task ExecuteAsync_HandlesInvalidConfiguration() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Invalid database configuration", response.Message); } @@ -377,11 +378,11 @@ public async Task ExecuteAsync_ValidatesRequiredParameters(string commandArgs, b // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); } else { - Assert.NotEqual(200, response.Status); + Assert.NotEqual(HttpStatusCode.OK, response.Status); if (expectedError != null) { Assert.Contains(expectedError, response.Message, StringComparison.OrdinalIgnoreCase); diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseDeleteCommandTests.cs index b97d739cdd..a31d9e556d 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseDeleteCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.Database; @@ -71,7 +72,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS Arg.Any(), Arg.Any(), Arg.Any(), - Arg.Any(), + Arg.Any(), Arg.Any()) .Returns(true); } @@ -82,7 +83,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.Equal("Success", response.Message); @@ -109,7 +110,7 @@ public async Task ExecuteAsync_DeletesDatabaseSuccessfully() var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -130,7 +131,7 @@ public async Task ExecuteAsync_IdempotentWhenDatabaseMissing() var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -150,7 +151,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var parseResult = _commandDefinition.Parse("--subscription sub1 --resource-group rg1 --server server1 --database db1"); var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -158,7 +159,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() [Fact] public async Task ExecuteAsync_Handles404Error() { - var requestFailed = new RequestFailedException(404, "Not found"); + var requestFailed = new RequestFailedException((int)HttpStatusCode.NotFound, "Not found"); _sqlService.DeleteDatabaseAsync( Arg.Any(), Arg.Any(), @@ -171,7 +172,7 @@ public async Task ExecuteAsync_Handles404Error() var parseResult = _commandDefinition.Parse("--subscription sub1 --resource-group rg1 --server server1 --database db1"); var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("SQL server or database not found", response.Message); } @@ -191,7 +192,7 @@ public async Task ExecuteAsync_Handles403Error() var parseResult = _commandDefinition.Parse("--subscription sub1 --resource-group rg1 --server server1 --database db1"); var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } @@ -239,7 +240,7 @@ public async Task ExecuteAsync_WithRetryPolicyOptions() var parseResult = _commandDefinition.Parse("--subscription sub1 --resource-group rg1 --server server1 --database db1 --retry-max-retries 5"); var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _sqlService.Received(1).DeleteDatabaseAsync( "server1", "db1", @@ -269,7 +270,7 @@ public async Task ExecuteAsync_HandlesVariousDatabaseNames(string dbName) var parseResult = _commandDefinition.Parse($"--subscription sub1 --resource-group rg1 --server server1 --database {dbName}"); var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _sqlService.Received(1).DeleteDatabaseAsync( "server1", dbName, @@ -295,7 +296,7 @@ public async Task ExecuteAsync_HandlesArgumentException() var parseResult = _commandDefinition.Parse("--subscription sub1 --resource-group rg1 --server server1 --database invalidDb"); var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Invalid database name", response.Message); } @@ -314,7 +315,7 @@ public async Task ExecuteAsync_VerifiesResultContainsExpectedData() var parseResult = _commandDefinition.Parse("--subscription sub1 --resource-group rg1 --server server1 --database db1"); var response = await _command.ExecuteAsync(_context, parseResult); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseListCommandTests.cs index e79685a991..817866e0b7 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.Database; @@ -76,12 +77,12 @@ public async Task ExecuteAsync_ValidatesInput(string args, bool shouldSucceed) // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } else { - Assert.NotEqual(200, response.Status); + Assert.NotEqual(HttpStatusCode.OK, response.Status); } } @@ -103,7 +104,7 @@ public async Task ExecuteAsync_HandlesServiceError() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.NotEqual(200, response.Status); + Assert.NotEqual(HttpStatusCode.OK, response.Status); Assert.Contains("Test error", response.Message); } @@ -131,7 +132,7 @@ public async Task ExecuteAsync_ReturnsExpectedDatabases() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); await _sqlService.Received(1).ListDatabasesAsync("test-server", "test-rg", "test-sub", Arg.Any(), Arg.Any()); diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseShowCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseShowCommandTests.cs index 25b74d1f48..4b233b9f8d 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseShowCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseShowCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.Database; @@ -86,7 +87,7 @@ public async Task ExecuteAsync_WithValidParameters_ReturnsDatabase() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -110,7 +111,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -134,7 +135,7 @@ public async Task ExecuteAsync_HandlesNotFoundDatabase() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message); } @@ -142,7 +143,7 @@ public async Task ExecuteAsync_HandlesNotFoundDatabase() public async Task ExecuteAsync_HandlesRequestFailedException() { // Arrange - var requestException = new RequestFailedException(404, "Database not found"); + var requestException = new RequestFailedException((int)HttpStatusCode.NotFound, "Database not found"); _sqlService.GetDatabaseAsync( Arg.Any(), Arg.Any(), @@ -158,7 +159,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("Database or server not found", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseUpdateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseUpdateCommandTests.cs index b120e1060a..e0f1a96265 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseUpdateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseUpdateCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.Database; @@ -106,7 +107,7 @@ public async Task ExecuteAsync_WithValidParameters_UpdatesDatabase() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -143,7 +144,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -152,7 +153,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_HandlesDatabaseNotFound() { // Arrange - var notFoundException = new RequestFailedException(404, "Database not found"); + var notFoundException = new RequestFailedException((int)HttpStatusCode.NotFound, "Database not found"); _sqlService.UpdateDatabaseAsync( Arg.Any(), Arg.Any(), @@ -181,7 +182,7 @@ public async Task ExecuteAsync_HandlesDatabaseNotFound() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message); } @@ -189,7 +190,7 @@ public async Task ExecuteAsync_HandlesDatabaseNotFound() public async Task ExecuteAsync_HandlesInvalidConfiguration() { // Arrange - var badRequestException = new RequestFailedException(400, "Invalid configuration"); + var badRequestException = new RequestFailedException((int)HttpStatusCode.BadRequest, "Invalid configuration"); _sqlService.UpdateDatabaseAsync( Arg.Any(), Arg.Any(), @@ -218,7 +219,7 @@ public async Task ExecuteAsync_HandlesInvalidConfiguration() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Invalid database configuration", response.Message); } @@ -280,11 +281,11 @@ public async Task ExecuteAsync_ValidatesRequiredParameters(string commandArgs, b // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); } else { - Assert.NotEqual(200, response.Status); + Assert.NotEqual(HttpStatusCode.OK, response.Status); if (expectedError != null) { Assert.Contains(expectedError, response.Message, StringComparison.OrdinalIgnoreCase); @@ -343,7 +344,7 @@ public async Task ExecuteAsync_WithMinimumRequiredParameters_Succeeds() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); @@ -415,7 +416,7 @@ public async Task ExecuteAsync_WithOptionalParameters_Succeeds(string commandArg var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -474,7 +475,7 @@ public async Task ExecuteAsync_WithSubscriptionFromEnvironment_Succeeds() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -517,7 +518,7 @@ public async Task ExecuteAsync_WithInvalidServerName_HandlesServiceError() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Invalid server name", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/ElasticPool/ElasticPoolListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/ElasticPool/ElasticPoolListCommandTests.cs index 1bad34309b..f08b87ba61 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/ElasticPool/ElasticPoolListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/ElasticPool/ElasticPoolListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.ElasticPool; @@ -88,7 +89,7 @@ public async Task ExecuteAsync_WithValidParameters_ReturnsElasticPools() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -114,7 +115,7 @@ public async Task ExecuteAsync_WithEmptyList_ReturnsEmptyResults() // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -137,7 +138,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -146,7 +147,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_HandlesRequestFailedException_NotFound() { // Arrange - var requestException = new RequestFailedException(404, "Server not found"); + var requestException = new RequestFailedException((int)HttpStatusCode.NotFound, "Server not found"); _sqlService.GetElasticPoolsAsync( Arg.Any(), Arg.Any(), @@ -161,7 +162,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException_NotFound() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("SQL server not found", response.Message); } @@ -184,7 +185,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException_Forbidden() var response = await _command.ExecuteAsync(_context, args); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } @@ -216,12 +217,12 @@ public async Task ExecuteAsync_ValidatesRequiredParameters(string args, bool sho // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/EntraAdmin/EntraAdminListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/EntraAdmin/EntraAdminListCommandTests.cs index 73da1d538c..4e4262989a 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/EntraAdmin/EntraAdminListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/EntraAdmin/EntraAdminListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.EntraAdmin; @@ -73,7 +74,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.Equal("Success", response.Message); @@ -110,7 +111,7 @@ public async Task ExecuteAsync_ReturnsAdministratorsSuccessfully() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -134,7 +135,7 @@ public async Task ExecuteAsync_ReturnsEmptyListWhenNoAdministrators() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -158,7 +159,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -167,7 +168,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_Handles404Error() { // Arrange - var requestException = new RequestFailedException(404, "Server not found"); + var requestException = new RequestFailedException((int)HttpStatusCode.NotFound, "Server not found"); _service.GetEntraAdministratorsAsync( Arg.Any(), Arg.Any(), @@ -183,7 +184,7 @@ public async Task ExecuteAsync_Handles404Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("SQL server not found", response.Message); } @@ -207,7 +208,7 @@ public async Task ExecuteAsync_Handles403Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleCreateCommandTests.cs index 634cd3f121..4166b00c7e 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleCreateCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.FirewallRule; @@ -86,7 +87,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.Equal("Success", response.Message); @@ -126,7 +127,7 @@ public async Task ExecuteAsync_CreatesFirewallRuleSuccessfully() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -153,7 +154,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -162,7 +163,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_Handles404Error() { // Arrange - var requestException = new RequestFailedException(404, "Server not found"); + var requestException = new RequestFailedException((int)HttpStatusCode.NotFound, "Server not found"); _service.CreateFirewallRuleAsync( Arg.Any(), Arg.Any(), @@ -181,7 +182,7 @@ public async Task ExecuteAsync_Handles404Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("SQL server not found", response.Message); } @@ -208,7 +209,7 @@ public async Task ExecuteAsync_Handles403Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } @@ -216,7 +217,7 @@ public async Task ExecuteAsync_Handles403Error() public async Task ExecuteAsync_Handles409Error() { // Arrange - var requestException = new RequestFailedException(409, "Conflict - rule already exists"); + var requestException = new RequestFailedException((int)HttpStatusCode.Conflict, "Conflict - rule already exists"); _service.CreateFirewallRuleAsync( Arg.Any(), Arg.Any(), @@ -235,7 +236,7 @@ public async Task ExecuteAsync_Handles409Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(409, response.Status); + Assert.Equal(HttpStatusCode.Conflict, response.Status); Assert.Contains("firewall rule with this name already exists", response.Message); } @@ -262,7 +263,7 @@ public async Task ExecuteAsync_HandlesArgumentException() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Invalid IP address format", response.Message); } @@ -342,7 +343,7 @@ public async Task ExecuteAsync_WithRetryPolicyOptions() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the service was called with retry policy @@ -389,7 +390,7 @@ public async Task ExecuteAsync_HandlesVariousIPFormats(string startIp, string en var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleDeleteCommandTests.cs index fe533a77ca..7075da6de6 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleDeleteCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.FirewallRule; @@ -82,7 +83,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.Equal("Success", response.Message); @@ -113,7 +114,7 @@ public async Task ExecuteAsync_DeletesFirewallRuleSuccessfully() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -138,7 +139,7 @@ public async Task ExecuteAsync_HandlesIdempotentDelete_WhenRuleDoesNotExist() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -163,7 +164,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -172,7 +173,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_Handles404Error() { // Arrange - var requestException = new RequestFailedException(404, "Server not found"); + var requestException = new RequestFailedException((int)HttpStatusCode.NotFound, "Server not found"); _service.DeleteFirewallRuleAsync( Arg.Any(), Arg.Any(), @@ -189,7 +190,7 @@ public async Task ExecuteAsync_Handles404Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("SQL server or firewall rule not found", response.Message); } @@ -214,7 +215,7 @@ public async Task ExecuteAsync_Handles403Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } @@ -272,7 +273,7 @@ public async Task ExecuteAsync_WithRetryPolicyOptions() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the service was called with retry policy @@ -310,7 +311,7 @@ public async Task ExecuteAsync_HandlesVariousRuleNames(string ruleName) var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the service was called with the correct rule name @@ -344,7 +345,7 @@ public async Task ExecuteAsync_HandlesArgumentException() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("Invalid firewall rule name", response.Message); } @@ -369,7 +370,7 @@ public async Task ExecuteAsync_VerifiesResultContainsExpectedData() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleListCommandTests.cs index c00d88fa12..b35bd75cf4 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.FirewallRule; @@ -73,7 +74,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.Equal("Success", response.Message); @@ -111,7 +112,7 @@ public async Task ExecuteAsync_ReturnsFirewallRulesSuccessfully() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -135,7 +136,7 @@ public async Task ExecuteAsync_ReturnsEmptyListWhenNoFirewallRules() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); Assert.Equal("Success", response.Message); } @@ -159,7 +160,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -168,7 +169,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_Handles404Error() { // Arrange - var requestException = new RequestFailedException(404, "Server not found"); + var requestException = new RequestFailedException((int)HttpStatusCode.NotFound, "Server not found"); _service.ListFirewallRulesAsync( Arg.Any(), Arg.Any(), @@ -184,7 +185,7 @@ public async Task ExecuteAsync_Handles404Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("SQL server not found", response.Message); } @@ -208,7 +209,7 @@ public async Task ExecuteAsync_Handles403Error() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } @@ -268,7 +269,7 @@ public async Task ExecuteAsync_WithRetryPolicyOptions() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); // Verify the service was called with retry policy diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerCreateCommandTests.cs index 0edde2a77a..6cd3f7757f 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerCreateCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.Server; @@ -95,7 +96,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.Equal("Success", response.Message); @@ -143,7 +144,7 @@ public async Task ExecuteAsync_CreatesServerSuccessfully() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -197,7 +198,7 @@ public async Task ExecuteAsync_WithOptionalParameters_PassesAllParameters() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); await _service.Received(1).CreateServerAsync( @@ -237,7 +238,7 @@ public async Task ExecuteAsync_WhenServiceThrowsException_ReturnsErrorResponse() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.NotEqual(200, response.Status); + Assert.NotEqual(HttpStatusCode.OK, response.Status); Assert.Contains("error", response.Message.ToLower()); } @@ -245,7 +246,7 @@ public async Task ExecuteAsync_WhenServiceThrowsException_ReturnsErrorResponse() public async Task ExecuteAsync_WhenServerAlreadyExists_Returns409StatusCode() { // Arrange - var requestException = new RequestFailedException(409, "Conflict: Server already exists"); + var requestException = new RequestFailedException((int)HttpStatusCode.Conflict, "Conflict: Server already exists"); _service.CreateServerAsync( Arg.Any(), @@ -267,7 +268,7 @@ public async Task ExecuteAsync_WhenServerAlreadyExists_Returns409StatusCode() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(409, response.Status); + Assert.Equal(HttpStatusCode.Conflict, response.Status); Assert.Contains("server with this name already exists", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerDeleteCommandTests.cs index 0f27d42d7d..947b435393 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerDeleteCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.Server; @@ -75,11 +76,11 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); } else { - Assert.NotEqual(200, response.Status); + Assert.NotEqual(HttpStatusCode.OK, response.Status); } } @@ -93,7 +94,7 @@ public async Task ExecuteAsync_WhenForceNotSpecified_ReturnsWarning() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Contains("WARNING", response.Message); Assert.Contains("permanently delete", response.Message); Assert.Contains("--force", response.Message); @@ -117,7 +118,7 @@ public async Task ExecuteAsync_WhenServerDeletedSuccessfully_ReturnsSuccess() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); await _service.Received(1).DeleteServerAsync("testserver", "rg", "sub", Arg.Any(), Arg.Any()); } @@ -140,7 +141,7 @@ public async Task ExecuteAsync_WhenServerNotFound_Returns404() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message); } @@ -162,7 +163,7 @@ public async Task ExecuteAsync_WhenServiceThrowsException_ReturnsErrorResponse() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.NotEqual(200, response.Status); + Assert.NotEqual(HttpStatusCode.OK, response.Status); Assert.Contains("error", response.Message.ToLower()); } @@ -170,7 +171,7 @@ public async Task ExecuteAsync_WhenServiceThrowsException_ReturnsErrorResponse() public async Task ExecuteAsync_WhenServerNotFoundFromAzure_Returns404StatusCode() { // Arrange - var requestException = new RequestFailedException(404, "Not Found: Server not found"); + var requestException = new RequestFailedException((int)HttpStatusCode.NotFound, "Not Found: Server not found"); _service.DeleteServerAsync( Arg.Any(), @@ -186,7 +187,7 @@ public async Task ExecuteAsync_WhenServerNotFoundFromAzure_Returns404StatusCode( var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message.ToLower()); } @@ -210,7 +211,7 @@ public async Task ExecuteAsync_WhenUnauthorized_Returns403StatusCode() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("authorization failed", response.Message.ToLower()); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerShowCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerShowCommandTests.cs index 1bf9eea368..4f7762574b 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerShowCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerShowCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Sql.Commands.Server; @@ -59,11 +60,11 @@ public async Task ExecuteAsync_ValidationScenarios_ReturnsExpectedResults(string if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message.ToLower()); } } @@ -98,7 +99,7 @@ public async Task ExecuteAsync_WithValidOptions_ReturnsServer() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -119,7 +120,7 @@ public async Task ExecuteAsync_WhenServerNotFound_ReturnsNotFound() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message.ToLower()); } @@ -127,7 +128,7 @@ public async Task ExecuteAsync_WhenServerNotFound_ReturnsNotFound() public async Task ExecuteAsync_WhenAzureRequestFails404_ReturnsNotFound() { // Arrange - var requestFailedException = new RequestFailedException(404, "Resource not found"); + var requestFailedException = new RequestFailedException((int)HttpStatusCode.NotFound, "Resource not found"); _service.GetServerAsync( Arg.Any(), Arg.Any(), @@ -141,7 +142,7 @@ public async Task ExecuteAsync_WhenAzureRequestFails404_ReturnsNotFound() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message.ToLower()); } @@ -163,7 +164,7 @@ public async Task ExecuteAsync_WhenAzureRequestFails403_ReturnsAuthorizationErro var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("authorization failed", response.Message.ToLower()); } @@ -184,7 +185,7 @@ public async Task ExecuteAsync_WhenArgumentException_ReturnsBadRequest() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("invalid parameter", response.Message.ToLower()); } @@ -205,7 +206,7 @@ public async Task ExecuteAsync_WhenUnexpectedException_ReturnsInternalServerErro var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Unexpected error", response.Message); } diff --git a/tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountCreateCommand.cs b/tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountCreateCommand.cs index 041b330210..44d6fa0564 100644 --- a/tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountCreateCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json.Serialization; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Commands.Subscription; @@ -109,9 +110,9 @@ public override async Task ExecuteAsync(CommandContext context, { RequestFailedException reqEx when reqEx.Status == 409 => "Storage account name already exists. Choose a different name.", - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed creating the storage account. Details: {reqEx.Message}", - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "Resource group not found. Verify the resource group exists and you have access.", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountCreateCommandTests.cs index e4b59fe631..d34a5d5c4c 100644 --- a/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountCreateCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -90,7 +91,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -112,7 +113,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS public async Task ExecuteAsync_HandlesStorageAccountNameAlreadyExists() { // Arrange - var conflictException = new RequestFailedException(409, "Storage account name already exists"); + var conflictException = new RequestFailedException((int)HttpStatusCode.Conflict, "Storage account name already exists"); _storageService.CreateStorageAccount( Arg.Any(), @@ -132,7 +133,7 @@ public async Task ExecuteAsync_HandlesStorageAccountNameAlreadyExists() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(409, response.Status); + Assert.Equal(HttpStatusCode.Conflict, response.Status); Assert.Contains("already exists", response.Message); } @@ -140,7 +141,7 @@ public async Task ExecuteAsync_HandlesStorageAccountNameAlreadyExists() public async Task ExecuteAsync_HandlesResourceGroupNotFound() { // Arrange - var notFoundException = new RequestFailedException(404, "Resource group not found"); + var notFoundException = new RequestFailedException((int)HttpStatusCode.NotFound, "Resource group not found"); _storageService.CreateStorageAccount( Arg.Any(), @@ -160,7 +161,7 @@ public async Task ExecuteAsync_HandlesResourceGroupNotFound() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("not found", response.Message); } @@ -188,7 +189,7 @@ public async Task ExecuteAsync_HandlesAuthorizationFailure() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } @@ -214,7 +215,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -259,7 +260,7 @@ public async Task ExecuteAsync_CallsServiceWithCorrectParameters() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _storageService.Received(1).CreateStorageAccount( "testaccount", "testrg", diff --git a/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountGetCommandTests.cs b/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountGetCommandTests.cs index f06be32dfb..3ab8629ba8 100644 --- a/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountGetCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Helpers; using Azure.Mcp.Core.Models.Command; @@ -125,7 +126,7 @@ public async Task ExecuteAsync_HandlesException() // Assert Assert.NotNull(response); - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith(expectedError, response.Message); } @@ -173,7 +174,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -213,7 +214,7 @@ public async Task ExecuteAsync_ReturnsAccountDetails_WhenAccountExists() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, StorageJsonContext.Default.AccountGetCommandResult); @@ -243,7 +244,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -257,7 +258,7 @@ public async Task ExecuteAsync_HandlesNotFound() _storageService.GetAccountDetails( Arg.Is(account), Arg.Is(subscription), Arg.Any(), Arg.Any()) - .ThrowsAsync(new RequestFailedException(404, "Storage account not found")); + .ThrowsAsync(new RequestFailedException((int)HttpStatusCode.NotFound, "Storage account not found")); var parseResult = _commandDefinition.Parse(["--account", account, "--subscription", subscription]); @@ -265,7 +266,7 @@ public async Task ExecuteAsync_HandlesNotFound() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("Storage account not found", response.Message); } @@ -286,7 +287,7 @@ public async Task ExecuteAsync_HandlesAuthorizationFailure() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Authorization failed", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/Hostpool/BaseHostPoolCommand.cs b/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/Hostpool/BaseHostPoolCommand.cs index 4c666eaf87..94676a551f 100644 --- a/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/Hostpool/BaseHostPoolCommand.cs +++ b/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/Hostpool/BaseHostPoolCommand.cs @@ -3,6 +3,7 @@ using System.CommandLine.Parsing; using System.Diagnostics.CodeAnalysis; +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Tools.VirtualDesktop.Options; @@ -53,7 +54,7 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe result.ErrorMessage = "Either --hostpool or --hostpool-resource-id must be provided."; if (commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = result.ErrorMessage; } return result; @@ -65,7 +66,7 @@ public override ValidationResult Validate(CommandResult commandResult, CommandRe result.ErrorMessage = "Cannot specify both --hostpool and --hostpool-resource-id. Use only one."; if (commandResponse != null) { - commandResponse.Status = 400; + commandResponse.Status = HttpStatusCode.BadRequest; commandResponse.Message = result.ErrorMessage; } return result; diff --git a/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/SessionHost/SessionHostListCommand.cs b/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/SessionHost/SessionHostListCommand.cs index a5e5237b3b..b1f08e5e6d 100644 --- a/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/SessionHost/SessionHostListCommand.cs +++ b/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/SessionHost/SessionHostListCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Models.Option; using Azure.Mcp.Tools.VirtualDesktop.Commands.Hostpool; @@ -90,9 +91,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException rfEx when rfEx.Status == 404 => + RequestFailedException rfEx when rfEx.Status == (int)HttpStatusCode.NotFound => "Hostpool not found. Verify the hostpool name and that you have access to it.", - RequestFailedException rfEx when rfEx.Status == 403 => + RequestFailedException rfEx when rfEx.Status == (int)HttpStatusCode.Forbidden => "Access denied. Verify you have the necessary permissions to access the hostpool.", RequestFailedException rfEx => rfEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/SessionHost/SessionHostUserSessionListCommand.cs b/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/SessionHost/SessionHostUserSessionListCommand.cs index 6008a643e4..5daeab18ee 100644 --- a/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/SessionHost/SessionHostUserSessionListCommand.cs +++ b/tools/Azure.Mcp.Tools.VirtualDesktop/src/Commands/SessionHost/SessionHostUserSessionListCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Tools.VirtualDesktop.Models; using Azure.Mcp.Tools.VirtualDesktop.Services; @@ -92,9 +93,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException rfEx when rfEx.Status == 404 => + RequestFailedException rfEx when rfEx.Status == (int)HttpStatusCode.NotFound => "Session host or hostpool not found. Verify the names and that you have access to them.", - RequestFailedException rfEx when rfEx.Status == 403 => + RequestFailedException rfEx when rfEx.Status == (int)HttpStatusCode.Forbidden => "Access denied. Verify you have the necessary permissions to access the session host and hostpool.", RequestFailedException rfEx => rfEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/Hostpool/HostpoolListCommandTests.cs b/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/Hostpool/HostpoolListCommandTests.cs index 692993b409..6ce5bb47db 100644 --- a/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/Hostpool/HostpoolListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/Hostpool/HostpoolListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.VirtualDesktop.Commands.Hostpool; @@ -80,12 +81,12 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("required", response.Message?.ToLower() ?? ""); } } @@ -105,7 +106,7 @@ public async Task ExecuteAsync_ReturnsEmptyResult_WhenNoHostpools() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } @@ -122,7 +123,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -145,7 +146,7 @@ public async Task ExecuteAsync_ReturnsHostpools_WhenSuccessful() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); await _virtualDesktopService.Received(1).ListHostpoolsAsync("test-sub", null, Arg.Any()); @@ -169,7 +170,7 @@ public async Task ExecuteAsync_CallsResourceGroupService_WhenResourceGroupProvid var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); await _virtualDesktopService.Received(1).ListHostpoolsByResourceGroupAsync("test-sub", "test-rg", null, Arg.Any()); @@ -194,7 +195,7 @@ public async Task ExecuteAsync_CallsSubscriptionService_WhenNoResourceGroup() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); await _virtualDesktopService.Received(1).ListHostpoolsAsync("test-sub", null, Arg.Any()); @@ -214,7 +215,7 @@ public async Task ExecuteAsync_ReturnsEmptyResult_WhenNoHostpoolsInResourceGroup var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } } diff --git a/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostListCommandTests.cs b/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostListCommandTests.cs index 3c432fcfb0..d39b175b49 100644 --- a/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.VirtualDesktop.Commands.SessionHost; @@ -100,7 +101,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.NotNull(response.Results); @@ -137,7 +138,7 @@ public async Task ExecuteAsync_WithValidInput_CallsServiceCorrectly() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -173,7 +174,7 @@ public async Task ExecuteAsync_WithResourceId_CallsServiceCorrectly() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -228,13 +229,13 @@ public async Task ExecuteAsync_WithResourceGroup_CallsServiceCorrectly() // Assert // If this fails, let's see what the actual message is - if (response.Status != 200) + if (response.Status != HttpStatusCode.OK) { Console.WriteLine($"Actual Status: {response.Status}"); Console.WriteLine($"Actual Message: {response.Message}"); } - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -271,7 +272,7 @@ public async Task ExecuteAsync_WithEmptyResults_ReturnsNullResults() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); } @@ -301,7 +302,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -322,7 +323,7 @@ public async Task ExecuteAsync_WithInvalidArgs_ReturnsBadRequest(string invalidA var response = await _command.ExecuteAsync(context, parseResult); // If parsing succeeds but validation fails, expect 400 - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); } catch (InvalidOperationException) { diff --git a/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostUserSessionListCommandTests.cs b/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostUserSessionListCommandTests.cs index 82ba06d6f3..f42dc1896b 100644 --- a/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostUserSessionListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostUserSessionListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.VirtualDesktop.Commands.SessionHost; @@ -114,12 +115,12 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS // Assert if (shouldSucceed) { - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); } else { - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.True(response.Message?.Contains("required", StringComparison.CurrentCultureIgnoreCase) == true || response.Message?.Contains("hostpool") == true || response.Message?.Contains("hostpool-resource-id") == true); @@ -166,7 +167,7 @@ public async Task ExecuteAsync_ReturnsUserSessionsSuccessfully() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -210,7 +211,7 @@ public async Task ExecuteAsync_WithResourceId_CallsServiceCorrectly() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -261,7 +262,7 @@ public async Task ExecuteAsync_WithResourceGroup_CallsServiceCorrectly() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -316,7 +317,7 @@ public async Task ExecuteAsync_ReturnsEmptyResultsWhenNoUserSessions() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); } @@ -347,7 +348,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test error", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -356,7 +357,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() public async Task ExecuteAsync_HandlesRequestFailedException_NotFound() { // Arrange - var exception = new RequestFailedException(404, "Session host not found"); + var exception = new RequestFailedException((int)HttpStatusCode.NotFound, "Session host not found"); _virtualDesktopService.ListUserSessionsAsync( Arg.Any(), Arg.Any(), @@ -379,7 +380,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException_NotFound() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("Session host or hostpool not found", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -411,7 +412,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException_Forbidden() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("Access denied", response.Message); Assert.Contains("troubleshooting", response.Message); } @@ -455,7 +456,7 @@ public async Task ExecuteAsync_WithTenantParameter() var response = await _command.ExecuteAsync(_context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); diff --git a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/CreateWorkbooksCommandTests.cs b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/CreateWorkbooksCommandTests.cs index 3eaa728ada..67b074821f 100644 --- a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/CreateWorkbooksCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/CreateWorkbooksCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; using Azure.Mcp.Tools.Workbooks.Commands.Workbooks; @@ -108,7 +109,7 @@ public async Task ExecuteAsync_CreatesWorkbook_WhenValidParametersProvided() // Assert Assert.Equal(context.Response, result); Assert.NotNull(result.Results); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); await _service.Received(1).CreateWorkbook( "test-sub", @@ -191,7 +192,7 @@ public async Task ExecuteAsync_ReturnsError_WhenServiceReturnsNull() // Assert Assert.Equal(context.Response, result); - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); } [Fact] @@ -218,7 +219,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() // Assert Assert.Equal(context.Response, result); - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); } [Fact] @@ -360,7 +361,7 @@ public async Task ExecuteAsync_WithInvalidDisplayName_ReturnsValidationError(str // Assert Assert.Equal(context.Response, result); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); } [Theory] @@ -385,7 +386,7 @@ public async Task ExecuteAsync_WithInvalidSerializedContent_ReturnsValidationErr // Assert Assert.Equal(context.Response, result); - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); } [Fact] @@ -449,7 +450,7 @@ public async Task ExecuteAsync_WithComplexSerializedContent_HandlesCorrectly() // Assert Assert.Equal(context.Response, result); - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); } @@ -518,7 +519,7 @@ public async Task ExecuteAsync_HandlesExceptionCorrectly_WhenExceptionOccurs() // Assert Assert.Equal(context.Response, result); - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); } private ParseResult CreateParseResult(params string[] args) diff --git a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/DeleteWorkbooksCommandTests.cs b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/DeleteWorkbooksCommandTests.cs index 2e9b82816c..4292d322a8 100644 --- a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/DeleteWorkbooksCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/DeleteWorkbooksCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -78,7 +79,7 @@ public async Task ExecuteAsync_ReturnsSuccess_WhenWorkbookDeletedSuccessfully() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.DeleteWorkbooksCommandResult); @@ -110,7 +111,7 @@ public async Task ExecuteAsync_ReturnsError_WhenWorkbookDeletionFails() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains($"Failed to delete workbook with ID '{workbookId}'", response.Message); Assert.Contains("troubleshooting", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -137,7 +138,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Service error", response.Message); Assert.Contains("troubleshooting", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -244,7 +245,7 @@ public async Task ExecuteAsync_WithInvalidWorkbookId_ReturnsValidationError(stri var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("workbook", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -260,7 +261,7 @@ public async Task ExecuteAsync_WithMissingWorkbookId_ReturnsValidationError() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("workbook", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -286,7 +287,7 @@ public async Task ExecuteAsync_WithValidResourceId_ProcessesCorrectly() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); @@ -319,7 +320,7 @@ public async Task ExecuteAsync_WithDisplayNameAsWorkbookId_ProcessesCorrectly() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results); diff --git a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/ListWorkbooksCommandTests.cs b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/ListWorkbooksCommandTests.cs index 6a15981d95..f45e1e92b5 100644 --- a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/ListWorkbooksCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/ListWorkbooksCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -125,7 +126,7 @@ public async Task ExecuteAsync_ReturnsWorkbooks_WhenWorkbooksExist() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.ListWorkbooksCommandResult); @@ -171,7 +172,7 @@ public async Task ExecuteAsync_ReturnsEmptyResults_WhenNoWorkbooksExist() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.ListWorkbooksCommandResult); @@ -206,7 +207,7 @@ public async Task ExecuteAsync_ReturnsEmptyResults_WhenServiceReturnsNull() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.ListWorkbooksCommandResult); @@ -239,7 +240,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Service error", response.Message); Assert.Contains("troubleshooting", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -359,7 +360,7 @@ public async Task ExecuteAsync_WithInvalidResourceGroup_ReturnsValidationError(s var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("resource", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -475,7 +476,7 @@ public async Task ExecuteAsync_WithKindFilter_PassesCorrectFilter() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _service.Received(1).ListWorkbooks( "sub123", @@ -528,7 +529,7 @@ public async Task ExecuteAsync_WithCategoryFilter_PassesCorrectFilter() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _service.Received(1).ListWorkbooks( "sub123", @@ -582,7 +583,7 @@ public async Task ExecuteAsync_WithSourceIdFilter_PassesCorrectFilter() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _service.Received(1).ListWorkbooks( "sub123", @@ -638,7 +639,7 @@ public async Task ExecuteAsync_WithMultipleFilters_PassesCorrectFilters() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _service.Received(1).ListWorkbooks( "sub123", @@ -694,7 +695,7 @@ public async Task ExecuteAsync_WithAllFiltersApplied_PassesCorrectFilters() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _service.Received(1).ListWorkbooks( "sub123", @@ -746,7 +747,7 @@ public async Task ExecuteAsync_WithoutFilters_PassesEmptyFilters() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _service.Received(1).ListWorkbooks( "sub123", @@ -783,7 +784,7 @@ public async Task ExecuteAsync_WithValidKind_AcceptsKindValue(string kind) // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _service.Received(1).ListWorkbooks( "sub123", @@ -822,7 +823,7 @@ public async Task ExecuteAsync_WithValidCategory_AcceptsCategoryValue(string cat // Assert Assert.NotNull(response); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); await _service.Received(1).ListWorkbooks( "sub123", diff --git a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/ShowWorkbooksCommandTests.cs b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/ShowWorkbooksCommandTests.cs index 3537dc7bff..81c86bd2ef 100644 --- a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/ShowWorkbooksCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/ShowWorkbooksCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -106,7 +107,7 @@ public async Task ExecuteAsync_ReturnsWorkbook_WhenWorkbookExists() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.ShowWorkbooksCommandResult); @@ -141,7 +142,7 @@ public async Task ExecuteAsync_ReturnsError_WhenWorkbookNotFound() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Failed to retrieve workbook", response.Message); Assert.Contains("troubleshooting", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -168,7 +169,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Service error", response.Message); Assert.Contains("troubleshooting", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -317,7 +318,7 @@ public async Task ExecuteAsync_WithInvalidWorkbookId_ReturnsValidationError(stri var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("workbook", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -336,7 +337,7 @@ public async Task ExecuteAsync_WithMalformedWorkbookId_ReturnsServiceError() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("workbook", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -414,7 +415,7 @@ public async Task ExecuteAsync_WithComplexWorkbookData_SerializesCorrectly() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.ShowWorkbooksCommandResult); diff --git a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/UpdateWorkbooksCommandTests.cs b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/UpdateWorkbooksCommandTests.cs index df75a81128..7c5d246831 100644 --- a/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/UpdateWorkbooksCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Workbooks/tests/Azure.Mcp.Tools.Workbooks.UnitTests/UpdateWorkbooksCommandTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using System.Text.Json; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Core.Options; @@ -109,7 +110,7 @@ public async Task ExecuteAsync_UpdatesWorkbook_WhenValidParametersProvided() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.UpdateWorkbooksCommandResult); @@ -160,7 +161,7 @@ public async Task ExecuteAsync_UpdatesOnlyDisplayName_WhenOnlyDisplayNameProvide // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.UpdateWorkbooksCommandResult); @@ -210,7 +211,7 @@ public async Task ExecuteAsync_UpdatesOnlySerializedContent_WhenOnlySerializedCo // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.UpdateWorkbooksCommandResult); @@ -296,7 +297,7 @@ public async Task ExecuteAsync_ReturnsError_WhenServiceReturnsNull() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Failed to update workbook", response.Message); } @@ -325,7 +326,7 @@ public async Task ExecuteAsync_HandlesServiceErrors() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Service error", response.Message); Assert.Contains("troubleshooting", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -350,7 +351,7 @@ public async Task ExecuteAsync_WithInvalidWorkbookId_ReturnsValidationError(stri var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(400, response.Status); + Assert.Equal(HttpStatusCode.BadRequest, response.Status); Assert.Contains("workbook", response.Message, StringComparison.OrdinalIgnoreCase); } @@ -419,7 +420,7 @@ public async Task ExecuteAsync_WithComplexSerializedContent_HandlesCorrectly() // Assert Assert.NotNull(response); Assert.NotNull(response.Results); - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); var json = JsonSerializer.Serialize(response.Results); var result = JsonSerializer.Deserialize(json, WorkbooksJsonContext.Default.UpdateWorkbooksCommandResult); @@ -607,7 +608,7 @@ public async Task ExecuteAsync_HandlesExceptionCorrectly_WhenExceptionOccurs() var response = await _command.ExecuteAsync(context, args); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.Contains("Test exception", response.Message); } } diff --git a/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/BestPractices/GetBestPracticesCommand.cs b/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/BestPractices/GetBestPracticesCommand.cs index 77619acfe5..453851d6bc 100644 --- a/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/BestPractices/GetBestPracticesCommand.cs +++ b/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/BestPractices/GetBestPracticesCommand.cs @@ -1,6 +1,7 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.PublicApi.Options; @@ -70,7 +71,7 @@ public override Task ExecuteAsync(CommandContext context, Parse catch (ArgumentException argEx) { _logger.LogError(argEx, "No best practice resources found for {}", options.Topic); - context.Response.Status = 404; + context.Response.Status = HttpStatusCode.NotFound; context.Response.Message = $"No best practice resources found for {options.Topic}"; } catch (Exception ex) diff --git a/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/BestPractices/GetWorkloadDefinitionCommand.cs b/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/BestPractices/GetWorkloadDefinitionCommand.cs index db620c0fd5..06f4c764cc 100644 --- a/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/BestPractices/GetWorkloadDefinitionCommand.cs +++ b/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/BestPractices/GetWorkloadDefinitionCommand.cs @@ -1,6 +1,7 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.PublicApi.Options; @@ -67,7 +68,7 @@ public override Task ExecuteAsync(CommandContext context, Parse catch (ArgumentException argEx) { _logger.LogError(argEx, "Invalid argument for workload {}", options.WorkloadType); - context.Response.Status = 404; + context.Response.Status = HttpStatusCode.NotFound; context.Response.Message = $"No item definition found for workload {options.WorkloadType}."; } catch (Exception ex) diff --git a/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/PublicApis/GetWorkloadApisCommand.cs b/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/PublicApis/GetWorkloadApisCommand.cs index a4178d63b6..999bd51e69 100644 --- a/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/PublicApis/GetWorkloadApisCommand.cs +++ b/tools/Fabric.Mcp.Tools.PublicApi/src/Commands/PublicApis/GetWorkloadApisCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.PublicApi.Options; @@ -63,7 +64,7 @@ public override async Task ExecuteAsync(CommandContext context, { if (options.WorkloadType!.Equals("common", StringComparison.OrdinalIgnoreCase)) { - context.Response.Status = 404; + context.Response.Status = HttpStatusCode.NotFound; context.Response.Message = "No workload of type 'common' exists. Did you mean 'platform'?. A full list of supported workloads can be found using the discover-workloads command"; return context.Response; } @@ -78,12 +79,12 @@ public override async Task ExecuteAsync(CommandContext context, _logger.LogError(httpEx, "HTTP error getting Fabric public APIs for workload {}", options.WorkloadType); if (httpEx.StatusCode == System.Net.HttpStatusCode.NotFound) { - context.Response.Status = 404; + context.Response.Status = HttpStatusCode.NotFound; context.Response.Message = $"No workload of type '{options.WorkloadType}' exists. A full list of supported workloads can be found using the discover-workloads command"; } else { - context.Response.Status = (int)(httpEx.StatusCode ?? System.Net.HttpStatusCode.InternalServerError); + context.Response.Status = httpEx.StatusCode ?? HttpStatusCode.InternalServerError; context.Response.Message = httpEx.Message; } } diff --git a/tools/Fabric.Mcp.Tools.PublicApi/tests/Commands/BestPracticesCommandsTests.cs b/tools/Fabric.Mcp.Tools.PublicApi/tests/Commands/BestPracticesCommandsTests.cs index 5ad6e4c16b..97ed3c9547 100644 --- a/tools/Fabric.Mcp.Tools.PublicApi/tests/Commands/BestPracticesCommandsTests.cs +++ b/tools/Fabric.Mcp.Tools.PublicApi/tests/Commands/BestPracticesCommandsTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure.Mcp.Core.Models.Command; using Fabric.Mcp.Tools.PublicApi.Commands.BestPractices; using Fabric.Mcp.Tools.PublicApi.Services; @@ -71,7 +72,7 @@ public async Task GetBestPracticesCommand_ExecuteAsync_WithValidTopic_ReturnsBes var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); fabricService.Received(1).GetTopicBestPractices("pagination"); } @@ -95,7 +96,7 @@ public async Task GetBestPracticesCommand_ExecuteAsync_WithEmptyTopic_ReturnsBad var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Equal("Missing Required options: --topic", result.Message); fabricService.DidNotReceive().GetTopicBestPractices(Arg.Any()); } @@ -121,7 +122,7 @@ public async Task GetBestPracticesCommand_ExecuteAsync_WithInvalidTopic_ReturnsN var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, result.Status); + Assert.Equal(HttpStatusCode.NotFound, result.Status); Assert.Contains("No best practice resources found for invalid-topic", result.Message); } @@ -146,7 +147,7 @@ public async Task GetBestPracticesCommand_ExecuteAsync_WithServiceException_Retu var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.NotEmpty(result.Message); } @@ -213,7 +214,7 @@ public async Task GetExamplesCommand_ExecuteAsync_WithValidWorkloadType_ReturnsE var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); await fabricService.Received(1).GetWorkloadExamplesAsync("notebook"); } @@ -237,7 +238,7 @@ public async Task GetExamplesCommand_ExecuteAsync_WithEmptyWorkloadType_ReturnsB var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Equal("Missing Required options: --workload-type", result.Message); await fabricService.DidNotReceive().GetWorkloadExamplesAsync(Arg.Any()); } @@ -263,7 +264,7 @@ public async Task GetExamplesCommand_ExecuteAsync_WithServiceException_ReturnsIn var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.NotEmpty(result.Message); } @@ -326,7 +327,7 @@ public async Task GetWorkloadDefinitionCommand_ExecuteAsync_WithValidWorkloadTyp var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); fabricService.Received(1).GetWorkloadItemDefinition("notebook"); } @@ -350,7 +351,7 @@ public async Task GetWorkloadDefinitionCommand_ExecuteAsync_WithEmptyWorkloadTyp var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Equal("Missing Required options: --workload-type", result.Message); fabricService.DidNotReceive().GetWorkloadItemDefinition(Arg.Any()); } @@ -376,7 +377,7 @@ public async Task GetWorkloadDefinitionCommand_ExecuteAsync_WithInvalidWorkloadT var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, result.Status); + Assert.Equal(HttpStatusCode.NotFound, result.Status); Assert.Contains("No item definition found for workload invalid-workload", result.Message); } @@ -401,7 +402,7 @@ public async Task GetWorkloadDefinitionCommand_ExecuteAsync_WithServiceException var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.NotEmpty(result.Message); } diff --git a/tools/Fabric.Mcp.Tools.PublicApi/tests/Commands/PublicApisCommandsTests.cs b/tools/Fabric.Mcp.Tools.PublicApi/tests/Commands/PublicApisCommandsTests.cs index 2e3747a70a..9b7f30f486 100644 --- a/tools/Fabric.Mcp.Tools.PublicApi/tests/Commands/PublicApisCommandsTests.cs +++ b/tools/Fabric.Mcp.Tools.PublicApi/tests/Commands/PublicApisCommandsTests.cs @@ -70,7 +70,7 @@ public async Task DiscoverPublicWorkloadsCommand_ExecuteAsync_ReturnsWorkloads() var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); await fabricService.Received(1).ListWorkloadsAsync(); } @@ -96,7 +96,7 @@ public async Task DiscoverPublicWorkloadsCommand_ExecuteAsync_HandlesException() var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.NotEmpty(result.Message); } @@ -154,7 +154,7 @@ public async Task GetPlatformApisCommand_ExecuteAsync_ReturnsPlatformApis() var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); await fabricService.Received(1).GetWorkloadPublicApis("platform"); } @@ -180,7 +180,7 @@ public async Task GetPlatformApisCommand_ExecuteAsync_HandlesException() var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.NotEmpty(result.Message); } @@ -239,7 +239,7 @@ public async Task GetWorkloadApisCommand_ExecuteAsync_WithValidWorkloadType_Retu var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, result.Status); + Assert.Equal(HttpStatusCode.OK, result.Status); Assert.NotNull(result.Results); await fabricService.Received(1).GetWorkloadPublicApis("notebook"); } @@ -263,7 +263,7 @@ public async Task GetWorkloadApisCommand_ExecuteAsync_WithEmptyWorkloadType_Retu var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(400, result.Status); + Assert.Equal(HttpStatusCode.BadRequest, result.Status); Assert.Equal("Missing Required options: --workload-type", result.Message); await fabricService.DidNotReceive().GetWorkloadPublicApis(Arg.Any()); } @@ -287,7 +287,7 @@ public async Task GetWorkloadApisCommand_ExecuteAsync_WithCommonWorkloadType_Ret var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, result.Status); + Assert.Equal(HttpStatusCode.NotFound, result.Status); Assert.Contains("No workload of type 'common' exists", result.Message); Assert.Contains("Did you mean 'platform'?", result.Message); await fabricService.DidNotReceive().GetWorkloadPublicApis(Arg.Any()); @@ -315,7 +315,7 @@ public async Task GetWorkloadApisCommand_ExecuteAsync_WithHttpNotFoundError_Retu var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, result.Status); + Assert.Equal(HttpStatusCode.NotFound, result.Status); Assert.Contains("No workload of type 'invalid-workload' exists", result.Message); Assert.Contains("discover-workloads command", result.Message); } @@ -342,7 +342,7 @@ public async Task GetWorkloadApisCommand_ExecuteAsync_WithHttpError_ReturnsMappe var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(503, result.Status); + Assert.Equal(HttpStatusCode.ServiceUnavailable, result.Status); Assert.Equal("Service unavailable", result.Message); } @@ -367,7 +367,7 @@ public async Task GetWorkloadApisCommand_ExecuteAsync_WithGeneralException_Retur var result = await command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, result.Status); + Assert.Equal(HttpStatusCode.InternalServerError, result.Status); Assert.NotEmpty(result.Message); } From c154d002edf856b52451c4da89f8c3afe25fb2eb Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Fri, 19 Sep 2025 19:33:45 -0700 Subject: [PATCH 08/10] Refactor HTTP status code handling to use HttpStatusCode enum for improved clarity and consistency across commands and tests. --- servers/Azure.Mcp.Server/src/Program.cs | 2 +- servers/Fabric.Mcp.Server/src/Program.cs | 2 +- servers/Template.Mcp.Server/src/Program.cs | 2 +- .../Cluster/ClusterGetCommandTests.cs | 2 +- .../FileSystem/FileSystemListCommandTests.cs | 2 +- .../FirewallRule/FirewallRuleCreateCommand.cs | 2 +- .../Commands/Server/ServerDeleteCommand.cs | 2 +- .../src/Commands/Server/ServerListCommand.cs | 5 +++-- .../src/Services/SqlService.cs | 2 +- .../Database/DatabaseCreateCommandTests.cs | 2 +- .../Database/DatabaseDeleteCommandTests.cs | 2 +- .../ElasticPoolListCommandTests.cs | 2 +- .../EntraAdmin/EntraAdminListCommandTests.cs | 2 +- .../FirewallRuleCreateCommandTests.cs | 2 +- .../FirewallRuleDeleteCommandTests.cs | 2 +- .../FirewallRuleListCommandTests.cs | 2 +- .../Server/ServerDeleteCommandTests.cs | 2 +- .../Server/ServerListCommandTests.cs | 21 ++++++++++--------- .../Server/ServerShowCommandTests.cs | 2 +- .../Commands/Account/AccountCreateCommand.cs | 2 +- .../Account/AccountCreateCommandTests.cs | 2 +- .../Account/AccountGetCommandTests.cs | 2 +- .../SessionHostUserSessionListCommandTests.cs | 2 +- 23 files changed, 35 insertions(+), 33 deletions(-) diff --git a/servers/Azure.Mcp.Server/src/Program.cs b/servers/Azure.Mcp.Server/src/Program.cs index 8a35bd88fd..f926f231bb 100644 --- a/servers/Azure.Mcp.Server/src/Program.cs +++ b/servers/Azure.Mcp.Server/src/Program.cs @@ -40,7 +40,7 @@ private static async Task Main(string[] args) var parseResult = rootCommand.Parse(args); var status = await parseResult.InvokeAsync(); - return (status >= 200 && status < 300) ? 0 : 1; + return (status >= (int)HttpStatusCode.OK && status < (int)HttpStatusCode.MultipleChoices) ? 0 : 1; } catch (Exception ex) { diff --git a/servers/Fabric.Mcp.Server/src/Program.cs b/servers/Fabric.Mcp.Server/src/Program.cs index 7d9dab3baf..335ec9e323 100644 --- a/servers/Fabric.Mcp.Server/src/Program.cs +++ b/servers/Fabric.Mcp.Server/src/Program.cs @@ -44,7 +44,7 @@ private static async Task Main(string[] args) var parseResult = rootCommand.Parse(args); var status = await parseResult.InvokeAsync(); - return (status >= 200 && status < 300) ? 0 : 1; + return (status >= (int)HttpStatusCode.OK && status < (int)HttpStatusCode.MultipleChoices) ? 0 : 1; } catch (Exception ex) { diff --git a/servers/Template.Mcp.Server/src/Program.cs b/servers/Template.Mcp.Server/src/Program.cs index a3c7c4bef7..aef2a62bc6 100644 --- a/servers/Template.Mcp.Server/src/Program.cs +++ b/servers/Template.Mcp.Server/src/Program.cs @@ -41,7 +41,7 @@ private static async Task Main(string[] args) var parseResult = rootCommand.Parse(args); var status = await parseResult.InvokeAsync(); - return (status >= 200 && status < 300) ? 0 : 1; + return (status >= (int)HttpStatusCode.OK && status < (int)HttpStatusCode.MultipleChoices) ? 0 : 1; } catch (Exception ex) { diff --git a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterGetCommandTests.cs b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterGetCommandTests.cs index a76cd04a7e..3af507f286 100644 --- a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterGetCommandTests.cs @@ -185,7 +185,7 @@ public async Task ExecuteAsync_Handles404NotFound() public async Task ExecuteAsync_Handles403Forbidden() { // Arrange - var forbiddenException = new RequestFailedException(403, "Forbidden"); + var forbiddenException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Forbidden"); _aksService.GetCluster(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(Task.FromException(forbiddenException)); diff --git a/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemListCommandTests.cs b/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemListCommandTests.cs index a6e2f4ca0c..c109cb1640 100644 --- a/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.AzureManagedLustre/tests/Azure.Mcp.Tools.AzureManagedLustre.UnitTests/FileSystem/FileSystemListCommandTests.cs @@ -230,7 +230,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException_Forbidden() // Arrange - 403 Forbidden _amlfsService.ListFileSystemsAsync( Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) - .ThrowsAsync(new RequestFailedException(403, "forbidden")); + .ThrowsAsync(new RequestFailedException((int)HttpStatusCode.Forbidden, "forbidden")); var args = _commandDefinition.Parse(["--subscription", _knownSubscriptionId]); var response = await _command.ExecuteAsync(_context, args); 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 59fa34bf5d..f90a17890a 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/FirewallRule/FirewallRuleCreateCommand.cs @@ -97,7 +97,7 @@ public override async Task ExecuteAsync(CommandContext context, "SQL server not found. Verify the server name, resource group, and that you have access.", RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed creating the firewall rule. Verify you have appropriate permissions. Details: {reqEx.Message}", - RequestFailedException reqEx when reqEx.Status == 409 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Conflict => "A firewall rule with this name already exists. Choose a different name or update the existing rule.", RequestFailedException reqEx => reqEx.Message, ArgumentException argEx => $"Invalid IP address format: {argEx.Message}", 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 ef5279bd72..05ade10bfa 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerDeleteCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerDeleteCommand.cs @@ -107,7 +107,7 @@ public override async Task ExecuteAsync(CommandContext context, $"The given SQL server not found. It may have already been deleted.", RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed deleting the SQL server. Verify you have appropriate permissions. Details: {reqEx.Message}", - RequestFailedException reqEx when reqEx.Status == 409 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Conflict => $"Cannot delete SQL server due to a conflict. It may be in use or have dependent resources. Details: {reqEx.Message}", RequestFailedException reqEx => reqEx.Message, ArgumentException argEx => $"Invalid parameter: {argEx.Message}", diff --git a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerListCommand.cs b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerListCommand.cs index 6ba917d2f3..92a6a0bbd2 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerListCommand.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Commands/Server/ServerListCommand.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine.Parsing; +using System.Net; using Azure; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; @@ -96,9 +97,9 @@ public override async Task ExecuteAsync(CommandContext context, protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 403 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed listing SQL servers. Verify you have appropriate permissions. Details: {reqEx.Message}", - RequestFailedException reqEx when reqEx.Status == 404 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound => "No SQL servers found for the specified resource group. Verify the resource group and subscription.", RequestFailedException reqEx => reqEx.Message, _ => base.GetErrorMessage(ex) diff --git a/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs b/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs index 72625d31e0..a78e317f71 100644 --- a/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs +++ b/tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs @@ -729,7 +729,7 @@ public async Task> ListServersAsync( var response = await subscriptionResource.GetResourceGroupAsync(resourceGroup, cancellationToken); resourceGroupResource = response.Value; } - catch (RequestFailedException reqEx) when (reqEx.Status == 404) + catch (RequestFailedException reqEx) when (reqEx.Status == (int)HttpStatusCode.NotFound) { _logger.LogWarning(reqEx, "Resource group not found when listing SQL servers. ResourceGroup: {ResourceGroup}, Subscription: {Subscription}", diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseCreateCommandTests.cs index db55347510..0193057bc3 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseCreateCommandTests.cs @@ -263,7 +263,7 @@ public async Task ExecuteAsync_HandlesServerNotFound() public async Task ExecuteAsync_HandlesAuthorizationFailure() { // Arrange - var authException = new RequestFailedException(403, "Authorization failed"); + var authException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Authorization failed"); _sqlService.CreateDatabaseAsync( Arg.Any(), Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseDeleteCommandTests.cs index a31d9e556d..11335b5d8b 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Database/DatabaseDeleteCommandTests.cs @@ -179,7 +179,7 @@ public async Task ExecuteAsync_Handles404Error() [Fact] public async Task ExecuteAsync_Handles403Error() { - var requestFailed = new RequestFailedException(403, "Access denied"); + var requestFailed = new RequestFailedException((int)HttpStatusCode.Forbidden, "Access denied"); _sqlService.DeleteDatabaseAsync( Arg.Any(), Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/ElasticPool/ElasticPoolListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/ElasticPool/ElasticPoolListCommandTests.cs index f08b87ba61..69c77257fa 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/ElasticPool/ElasticPoolListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/ElasticPool/ElasticPoolListCommandTests.cs @@ -170,7 +170,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException_NotFound() public async Task ExecuteAsync_HandlesRequestFailedException_Forbidden() { // Arrange - var requestException = new RequestFailedException(403, "Forbidden"); + var requestException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Forbidden"); _sqlService.GetElasticPoolsAsync( Arg.Any(), Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/EntraAdmin/EntraAdminListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/EntraAdmin/EntraAdminListCommandTests.cs index 4e4262989a..9c26bc3ea3 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/EntraAdmin/EntraAdminListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/EntraAdmin/EntraAdminListCommandTests.cs @@ -192,7 +192,7 @@ public async Task ExecuteAsync_Handles404Error() public async Task ExecuteAsync_Handles403Error() { // Arrange - var requestException = new RequestFailedException(403, "Access denied"); + var requestException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Access denied"); _service.GetEntraAdministratorsAsync( Arg.Any(), Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleCreateCommandTests.cs index 4166b00c7e..879b3286b7 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleCreateCommandTests.cs @@ -190,7 +190,7 @@ public async Task ExecuteAsync_Handles404Error() public async Task ExecuteAsync_Handles403Error() { // Arrange - var requestException = new RequestFailedException(403, "Access denied"); + var requestException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Access denied"); _service.CreateFirewallRuleAsync( Arg.Any(), Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleDeleteCommandTests.cs index 7075da6de6..cc5f0fc057 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleDeleteCommandTests.cs @@ -198,7 +198,7 @@ public async Task ExecuteAsync_Handles404Error() public async Task ExecuteAsync_Handles403Error() { // Arrange - var requestException = new RequestFailedException(403, "Access denied"); + var requestException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Access denied"); _service.DeleteFirewallRuleAsync( Arg.Any(), Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleListCommandTests.cs index b35bd75cf4..e93811b0f0 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/FirewallRule/FirewallRuleListCommandTests.cs @@ -193,7 +193,7 @@ public async Task ExecuteAsync_Handles404Error() public async Task ExecuteAsync_Handles403Error() { // Arrange - var requestException = new RequestFailedException(403, "Access denied"); + var requestException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Access denied"); _service.ListFirewallRulesAsync( Arg.Any(), Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerDeleteCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerDeleteCommandTests.cs index 947b435393..85df263240 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerDeleteCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerDeleteCommandTests.cs @@ -195,7 +195,7 @@ public async Task ExecuteAsync_WhenServerNotFoundFromAzure_Returns404StatusCode( public async Task ExecuteAsync_WhenUnauthorized_Returns403StatusCode() { // Arrange - var requestException = new RequestFailedException(403, "Forbidden: Insufficient permissions"); + var requestException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Forbidden: Insufficient permissions"); _service.DeleteServerAsync( Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerListCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerListCommandTests.cs index a17dacd9a5..52c84d5d70 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerListCommandTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.CommandLine; +using System.Net; using Azure; using Azure.Mcp.Core.Models.Command; using Azure.Mcp.Tools.Sql.Commands.Server; @@ -99,7 +100,7 @@ public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldS var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(shouldSucceed ? 200 : 400, response.Status); + Assert.Equal(shouldSucceed ? HttpStatusCode.OK : HttpStatusCode.BadRequest, response.Status); if (shouldSucceed) { Assert.Equal("Success", response.Message); @@ -154,7 +155,7 @@ public async Task ExecuteAsync_ListsServersSuccessfully() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -185,7 +186,7 @@ public async Task ExecuteAsync_WithEmptyList_ReturnsSuccessfully() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.Equal("Success", response.Message); Assert.NotNull(response.Results); @@ -214,7 +215,7 @@ public async Task ExecuteAsync_WhenServiceThrowsException_ReturnsErrorResponse() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.NotEqual(200, response.Status); + Assert.NotEqual(HttpStatusCode.OK, response.Status); Assert.Contains("error", response.Message.ToLower()); } @@ -222,7 +223,7 @@ public async Task ExecuteAsync_WhenServiceThrowsException_ReturnsErrorResponse() public async Task ExecuteAsync_WhenAuthorizationFails_Returns403StatusCode() { // Arrange - var requestException = new RequestFailedException(403, "Forbidden: Insufficient permissions"); + var requestException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Forbidden: Insufficient permissions"); _service.ListServersAsync( Arg.Any(), @@ -238,7 +239,7 @@ public async Task ExecuteAsync_WhenAuthorizationFails_Returns403StatusCode() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(403, response.Status); + Assert.Equal(HttpStatusCode.Forbidden, response.Status); Assert.Contains("authorization failed", response.Message.ToLower()); Assert.Contains("verify you have appropriate permissions", response.Message.ToLower()); } @@ -247,7 +248,7 @@ public async Task ExecuteAsync_WhenAuthorizationFails_Returns403StatusCode() public async Task ExecuteAsync_WhenResourceGroupNotFound_Returns404StatusCode() { // Arrange - var requestException = new RequestFailedException(404, "Not Found: Resource group does not exist"); + var requestException = new RequestFailedException((int)HttpStatusCode.NotFound, "Not Found: Resource group does not exist"); _service.ListServersAsync( Arg.Any(), @@ -263,7 +264,7 @@ public async Task ExecuteAsync_WhenResourceGroupNotFound_Returns404StatusCode() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(404, response.Status); + Assert.Equal(HttpStatusCode.NotFound, response.Status); Assert.Contains("no sql servers found", response.Message.ToLower()); Assert.Contains("verify the resource group and subscription", response.Message.ToLower()); } @@ -272,7 +273,7 @@ public async Task ExecuteAsync_WhenResourceGroupNotFound_Returns404StatusCode() public async Task ExecuteAsync_WithGenericRequestFailedException_ReturnsOriginalMessage() { // Arrange - var requestException = new RequestFailedException(500, "Internal Server Error"); + var requestException = new RequestFailedException((int)HttpStatusCode.InternalServerError, "Internal Server Error"); _service.ListServersAsync( Arg.Any(), @@ -288,7 +289,7 @@ public async Task ExecuteAsync_WithGenericRequestFailedException_ReturnsOriginal var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(500, response.Status); + Assert.Equal(HttpStatusCode.InternalServerError, response.Status); Assert.StartsWith("Internal Server Error", response.Message); } } diff --git a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerShowCommandTests.cs b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerShowCommandTests.cs index 4f7762574b..6dd45b6c36 100644 --- a/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerShowCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Sql/tests/Azure.Mcp.Tools.Sql.UnitTests/Server/ServerShowCommandTests.cs @@ -150,7 +150,7 @@ public async Task ExecuteAsync_WhenAzureRequestFails404_ReturnsNotFound() public async Task ExecuteAsync_WhenAzureRequestFails403_ReturnsAuthorizationError() { // Arrange - var requestFailedException = new RequestFailedException(403, "Authorization failed"); + var requestFailedException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Authorization failed"); _service.GetServerAsync( Arg.Any(), Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountCreateCommand.cs b/tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountCreateCommand.cs index 44d6fa0564..900b2096e1 100644 --- a/tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountCreateCommand.cs +++ b/tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountCreateCommand.cs @@ -108,7 +108,7 @@ public override async Task ExecuteAsync(CommandContext context, // Implementation-specific error handling protected override string GetErrorMessage(Exception ex) => ex switch { - RequestFailedException reqEx when reqEx.Status == 409 => + RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Conflict => "Storage account name already exists. Choose a different name.", RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden => $"Authorization failed creating the storage account. Details: {reqEx.Message}", diff --git a/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountCreateCommandTests.cs b/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountCreateCommandTests.cs index d34a5d5c4c..c223c5da26 100644 --- a/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountCreateCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountCreateCommandTests.cs @@ -169,7 +169,7 @@ public async Task ExecuteAsync_HandlesResourceGroupNotFound() public async Task ExecuteAsync_HandlesAuthorizationFailure() { // Arrange - var authException = new RequestFailedException(403, "Authorization failed"); + var authException = new RequestFailedException((int)HttpStatusCode.Forbidden, "Authorization failed"); _storageService.CreateStorageAccount( Arg.Any(), diff --git a/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountGetCommandTests.cs b/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountGetCommandTests.cs index 3ab8629ba8..72801839ac 100644 --- a/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountGetCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.UnitTests/Account/AccountGetCommandTests.cs @@ -279,7 +279,7 @@ public async Task ExecuteAsync_HandlesAuthorizationFailure() _storageService.GetAccountDetails( Arg.Is(account), Arg.Is(subscription), Arg.Any(), Arg.Any()) - .ThrowsAsync(new RequestFailedException(403, "Authorization failed")); + .ThrowsAsync(new RequestFailedException((int)HttpStatusCode.Forbidden, "Authorization failed")); var parseResult = _commandDefinition.Parse(["--account", account, "--subscription", subscription]); diff --git a/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostUserSessionListCommandTests.cs b/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostUserSessionListCommandTests.cs index f42dc1896b..aa96607f81 100644 --- a/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostUserSessionListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.VirtualDesktop/tests/Azure.Mcp.Tools.VirtualDesktop.UnitTests/SessionHost/SessionHostUserSessionListCommandTests.cs @@ -389,7 +389,7 @@ public async Task ExecuteAsync_HandlesRequestFailedException_NotFound() public async Task ExecuteAsync_HandlesRequestFailedException_Forbidden() { // Arrange - var exception = new RequestFailedException(403, "Access denied"); + var exception = new RequestFailedException((int)HttpStatusCode.Forbidden, "Access denied"); _virtualDesktopService.ListUserSessionsAsync( Arg.Any(), Arg.Any(), From f3ebac82c7de4f0b6b67028321ff87840dff150a Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Mon, 22 Sep 2025 08:58:37 -0700 Subject: [PATCH 09/10] Refactor error handling in GetCosmosClientAsync to use HttpStatusCode enum for improved clarity and maintainability. --- tools/Azure.Mcp.Tools.Cosmos/src/Services/CosmosService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/Azure.Mcp.Tools.Cosmos/src/Services/CosmosService.cs b/tools/Azure.Mcp.Tools.Cosmos/src/Services/CosmosService.cs index 6042b35b77..b73b30125b 100644 --- a/tools/Azure.Mcp.Tools.Cosmos/src/Services/CosmosService.cs +++ b/tools/Azure.Mcp.Tools.Cosmos/src/Services/CosmosService.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net; using Azure.Mcp.Core.Options; using Azure.Mcp.Core.Services.Azure; using Azure.Mcp.Core.Services.Azure.Subscription; @@ -136,7 +137,7 @@ private async Task GetCosmosClientAsync( } catch (Exception ex) when ( authMethod == AuthMethod.Credential && - (ex.Message.Contains("401") || ex.Message.Contains("403"))) + (ex.Message.Contains(((int)HttpStatusCode.Unauthorized).ToString()) || ex.Message.Contains(((int)HttpStatusCode.Forbidden).ToString()))) { // If credential auth fails with 401/403, try key auth cosmosClient = await CreateCosmosClientWithAuth( From 9ae13d238ad0f22132952897cd316af75ca91d6d Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Mon, 22 Sep 2025 09:26:14 -0700 Subject: [PATCH 10/10] Refactor status code assertion in ExecuteAsync_EnrichedClusterFields_SerializeCorrectly test to use HttpStatusCode enum for improved clarity and consistency. --- .../Cluster/ClusterListCommandTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterListCommandTests.cs b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterListCommandTests.cs index 814e5fd788..c65aa37dd0 100644 --- a/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterListCommandTests.cs +++ b/tools/Azure.Mcp.Tools.Aks/tests/Azure.Mcp.Tools.Aks.UnitTests/Cluster/ClusterListCommandTests.cs @@ -203,7 +203,7 @@ public async Task ExecuteAsync_EnrichedClusterFields_SerializeCorrectly() var response = await _command.ExecuteAsync(context, parseResult); // Assert - Assert.Equal(200, response.Status); + Assert.Equal(HttpStatusCode.OK, response.Status); Assert.NotNull(response.Results); var json = JsonSerializer.Serialize(response.Results);