Repository: Azure/azure-mcp (2025-07-14T23:02:15Z)

Issue:
Remove SSE
No longer supported in MCP spec, so let's remove remote support entirely for now and implement HTTP Streamable in the near future


Discussion:
Here's  a link to it: https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse-deprecated
https://github.com/Azure/azure-mcp-pr/issues/300



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8919a0dbb..53b241fbc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@
 
 ### Breaking Changes
 
+- Removed SSE (Server-Sent Events) transport support. Only stdio transport is now supported as SSE is no longer part of the MCP specification. [#593](https://github.com/Azure/azure-mcp/issues/593)
+
 ### Bugs Fixed
 
 ### Other Changes
diff --git a/README.md b/README.md
index 62805e107..d9569dc79 100644
--- a/README.md
+++ b/README.md
@@ -246,7 +246,7 @@ AZURE_CLIENT_SECRET={YOUR_AZURE_CLIENT_SECRET}
 Optionally, customers can use `--env` or `--volume` to pass authentication values.
 
 #### 🤖 Custom MCP Client Install Steps (Optional)
-You can easily configure your MCP client to use the Azure MCP Server. Have your client run the following command and access it via standard IO or SSE.
+You can easily configure your MCP client to use the Azure MCP Server. Have your client run the following command and access it via standard IO.
 
 #### 🔧 Manual Install Steps (Optional)
 For a step-by-step installation, follow these instructions:
@@ -293,22 +293,6 @@ You can optionally set the `--namespace <namespace>` flag to install tools for t
 
 Configure the MCP client to execute: `npx -y @azure/mcp@latest server start`. For instructions on using , follow instructions in [Quick install with VS Code](#-quick-install-with-vs-code) or [Manual Install](#-manual-install).
 
-### Using SSE
-
-1. Open a terminal window and execute: `npx -y @azure/mcp@latest server start --transport sse`
-2. The server starts up and is hosted at: http://localhost:5008.  To use another port, append `--port {YOUR-PORT-NUMBER}`.
-3. Open your MCP client and add the SSE configuration value.  This may differ between MCP clients.  In VS Code, it will look like:
-   ```json
-   {
-      "servers": {
-        "Azure MCP Server": {
-          "type": "sse",
-          "url": "http://localhost:5008/sse"
-        }
-      }
-    }
-   ```
-
 More end-to-end MCP client/agent guides are coming soon!
 </details>
 
diff --git a/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs b/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs
index d6ad2fbe1..62c8bdb12 100644
--- a/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs
+++ b/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs
@@ -8,7 +8,7 @@ namespace AzureMcp.Areas.Server.Commands.Discovery;
 
 /// <summary>
 /// Provides an MCP server implementation based on registry configuration.
-/// Supports both SSE (Server-Sent Events) and stdio transport mechanisms.
+/// Supports stdio transport mechanism.
 /// </summary>
 /// <param name="id">The unique identifier for the server.</param>
 /// <param name="serverInfo">Configuration information for the server.</param>
@@ -39,37 +39,16 @@ public McpServerMetadata CreateMetadata()
     /// <exception cref="InvalidOperationException">Thrown when the server configuration doesn't specify a valid transport mechanism.</exception>
     public async Task<IMcpClient> CreateClientAsync(McpClientOptions clientOptions)
     {
-        if (!string.IsNullOrWhiteSpace(_serverInfo.Url))
-        {
-            return await CreateSseClientAsync(clientOptions);
-        }
-        else if (!string.IsNullOrWhiteSpace(_serverInfo.Type) && _serverInfo.Type.Equals("stdio", StringComparison.OrdinalIgnoreCase))
+        if (!string.IsNullOrWhiteSpace(_serverInfo.Type) && _serverInfo.Type.Equals("stdio", StringComparison.OrdinalIgnoreCase))
         {
             return await CreateStdioClientAsync(clientOptions);
         }
         else
         {
-            throw new InvalidOperationException($"Registry server '{_id}' does not have a valid url or type for transport.");
+            throw new InvalidOperationException($"Registry server '{_id}' does not have a valid transport type. Only 'stdio' is supported.");
         }
     }
 
-    /// <summary>
-    /// Creates an MCP client that communicates with the server using SSE (Server-Sent Events).
-    /// </summary>
-    /// <param name="clientOptions">Options to configure the client behavior.</param>
-    /// <returns>A configured MCP client using SSE transport.</returns>
-    private async Task<IMcpClient> CreateSseClientAsync(McpClientOptions clientOptions)
-    {
-        var transportOptions = new SseClientTransportOptions
-        {
-            Name = _id,
-            Endpoint = new Uri(_serverInfo.Url!),
-            TransportMode = HttpTransportMode.AutoDetect,
-        };
-        var clientTransport = new SseClientTransport(transportOptions);
-        return await McpClientFactory.CreateAsync(clientTransport, clientOptions);
-    }
-
     /// <summary>
     /// Creates an MCP client that communicates with the server using stdio (standard input/output).
     /// </summary>
diff --git a/src/Areas/Server/Commands/ServiceCollectionExtensions.cs b/src/Areas/Server/Commands/ServiceCollectionExtensions.cs
index c75f91c2b..a56893173 100644
--- a/src/Areas/Server/Commands/ServiceCollectionExtensions.cs
+++ b/src/Areas/Server/Commands/ServiceCollectionExtensions.cs
@@ -125,15 +125,7 @@ public static IServiceCollection AddAzureMcpServer(this IServiceCollection servi
             });
 
         var mcpServerBuilder = services.AddMcpServer();
-
-        if (serviceStartOptions.Transport != TransportTypes.Sse)
-        {
-            mcpServerBuilder.WithStdioServerTransport();
-        }
-        else
-        {
-            mcpServerBuilder.WithHttpTransport();
-        }
+        mcpServerBuilder.WithStdioServerTransport();
 
         return services;
     }
diff --git a/src/Areas/Server/Commands/ServiceStartCommand.cs b/src/Areas/Server/Commands/ServiceStartCommand.cs
index a05ad2283..a77c35a2b 100644
--- a/src/Areas/Server/Commands/ServiceStartCommand.cs
+++ b/src/Areas/Server/Commands/ServiceStartCommand.cs
@@ -4,9 +4,6 @@
 using AzureMcp.Areas.Server.Options;
 using AzureMcp.Commands;
 using AzureMcp.Models.Option;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Azure;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.Logging;
@@ -25,7 +22,6 @@ public sealed class ServiceStartCommand : BaseCommand
 {
     private const string CommandTitle = "Start MCP Server";
     private readonly Option<string> _transportOption = ServiceOptionDefinitions.Transport;
-    private readonly Option<int> _portOption = ServiceOptionDefinitions.Port;
     private readonly Option<string[]?> _namespaceOption = ServiceOptionDefinitions.Namespace;
     private readonly Option<string?> _modeOption = ServiceOptionDefinitions.Mode;
     private readonly Option<bool?> _readOnlyOption = ServiceOptionDefinitions.ReadOnly;
@@ -53,7 +49,6 @@ protected override void RegisterOptions(Command command)
     {
         base.RegisterOptions(command);
         command.AddOption(_transportOption);
-        command.AddOption(_portOption);
         command.AddOption(_namespaceOption);
         command.AddOption(_modeOption);
         command.AddOption(_readOnlyOption);
@@ -67,10 +62,6 @@ protected override void RegisterOptions(Command command)
     /// <returns>A command response indicating the result of the operation.</returns>
     public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult)
     {
-        var port = parseResult.GetValueForOption(_portOption) == default
-            ? ServiceOptionDefinitions.Port.GetDefaultValue()
-            : parseResult.GetValueForOption(_portOption);
-
         var namespaces = parseResult.GetValueForOption(_namespaceOption) == default
             ? ServiceOptionDefinitions.Namespace.GetDefaultValue()
             : parseResult.GetValueForOption(_namespaceOption);
@@ -86,7 +77,6 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
         var serverOptions = new ServiceStartOptions
         {
             Transport = parseResult.GetValueForOption(_transportOption) ?? TransportTypes.StdIo,
-            Port = port,
             Namespace = namespaces,
             Mode = mode,
             ReadOnly = readOnly,
@@ -106,46 +96,19 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
     /// <returns>An IHost instance configured for the MCP server.</returns>
     private IHost CreateHost(ServiceStartOptions serverOptions)
     {
-        if (serverOptions.Transport == TransportTypes.Sse)
-        {
-            var builder = WebApplication.CreateSlimBuilder([]);
-
-            builder.Services.AddLogging(); // For ILoggerFactory.
-            builder.Services.AddOptions(); // For IOptions<T> support.
-
-            Program.ConfigureServices(builder.Services);
-            ConfigureMcpServer(builder.Services, serverOptions);
-
-            builder.WebHost
-                .ConfigureKestrel(server => server.ListenAnyIP(serverOptions.Port))
-                .ConfigureLogging(logging =>
-                {
-                    logging.ConfigureOpenTelemetryLogger();
-                    logging.AddEventSourceLogger();
-                });
-
-            var application = builder.Build();
-
-            application.MapMcp();
-
-            return application;
-        }
-        else
-        {
-            return Host.CreateDefaultBuilder()
-                .ConfigureLogging(logging =>
-                {
-                    logging.ClearProviders();
-                    logging.ConfigureOpenTelemetryLogger();
-                    logging.AddEventSourceLogger();
-                })
-                .ConfigureServices(services =>
-                {
-                    Program.ConfigureServices(services);
-                    ConfigureMcpServer(services, serverOptions);
-                })
-                .Build();
-        }
+        return Host.CreateDefaultBuilder()
+            .ConfigureLogging(logging =>
+            {
+                logging.ClearProviders();
+                logging.ConfigureOpenTelemetryLogger();
+                logging.AddEventSourceLogger();
+            })
+            .ConfigureServices(services =>
+            {
+                Program.ConfigureServices(services);
+                ConfigureMcpServer(services, serverOptions);
+            })
+            .Build();
     }
 
     /// <summary>
diff --git a/src/Areas/Server/Commands/ToolLoading/RegistryToolLoader.cs b/src/Areas/Server/Commands/ToolLoading/RegistryToolLoader.cs
index 4599d9b78..a04718bb0 100644
--- a/src/Areas/Server/Commands/ToolLoading/RegistryToolLoader.cs
+++ b/src/Areas/Server/Commands/ToolLoading/RegistryToolLoader.cs
@@ -52,7 +52,17 @@ public async ValueTask<ListToolsResult> ListToolsHandler(RequestContext<ListTool
         foreach (var server in serverList)
         {
             var serverMetadata = server.CreateMetadata();
-            var mcpClient = await _serverDiscoveryStrategy.GetOrCreateClientAsync(serverMetadata.Name, ClientOptions);
+            IMcpClient? mcpClient;
+            try
+            {
+                mcpClient = await _serverDiscoveryStrategy.GetOrCreateClientAsync(serverMetadata.Name, ClientOptions);
+            }
+            catch (InvalidOperationException ex)
+            {
+                _logger.LogWarning("Failed to create client for provider {ProviderName}: {Error}", serverMetadata.Name, ex.Message);
+                continue;
+            }
+
             if (mcpClient == null)
             {
                 _logger.LogWarning("Failed to get MCP client for provider {ProviderName}.", serverMetadata.Name);
diff --git a/src/Areas/Server/Models/RegistryServerInfo.cs b/src/Areas/Server/Models/RegistryServerInfo.cs
index 9ca1a09a5..0815196d8 100644
--- a/src/Areas/Server/Models/RegistryServerInfo.cs
+++ b/src/Areas/Server/Models/RegistryServerInfo.cs
@@ -7,7 +7,7 @@ namespace AzureMcp.Areas.Server.Models;
 
 /// <summary>
 /// Contains configuration information for an MCP server defined in the registry.
-/// Supports both URL-based (SSE) and command-based (stdio) transport mechanisms.
+/// Supports command-based (stdio) transport mechanism.
 /// </summary>
 public sealed class RegistryServerInfo
 {
@@ -19,8 +19,7 @@ public sealed class RegistryServerInfo
     public string? Name { get; set; }
 
     /// <summary>
-    /// Gets the URL endpoint for SSE-based transport.
-    /// If provided, the server will use SSE transport instead of stdio.
+    /// Gets the URL endpoint (deprecated - no longer used).
     /// </summary>
     [JsonPropertyName("url")]
     public string? Url { get; init; }
diff --git a/src/Areas/Server/Options/ServiceOptionDefinitions.cs b/src/Areas/Server/Options/ServiceOptionDefinitions.cs
index 4fd50d1a0..b9711a33d 100644
--- a/src/Areas/Server/Options/ServiceOptionDefinitions.cs
+++ b/src/Areas/Server/Options/ServiceOptionDefinitions.cs
@@ -6,7 +6,6 @@ namespace AzureMcp.Areas.Server.Options;
 public static class ServiceOptionDefinitions
 {
     public const string TransportName = "transport";
-    public const string PortName = "port";
     public const string NamespaceName = "namespace";
     public const string ModeName = "mode";
     public const string ReadOnlyName = "read-only";
@@ -20,15 +19,6 @@ public static class ServiceOptionDefinitions
         IsRequired = false
     };
 
-    public static readonly Option<int> Port = new(
-        $"--{PortName}",
-        () => 5008,
-        "Port to use for Azure MCP Server."
-    )
-    {
-        IsRequired = false
-    };
-
     public static readonly Option<string[]?> Namespace = new(
         $"--{NamespaceName}",
         () => null,
diff --git a/src/Areas/Server/Options/ServiceStartOptions.cs b/src/Areas/Server/Options/ServiceStartOptions.cs
index 4d186d889..eb22c1872 100644
--- a/src/Areas/Server/Options/ServiceStartOptions.cs
+++ b/src/Areas/Server/Options/ServiceStartOptions.cs
@@ -17,12 +17,6 @@ public class ServiceStartOptions
     [JsonPropertyName("transport")]
     public string Transport { get; set; } = TransportTypes.StdIo;
 
-    /// <summary>
-    /// Gets or sets the port number for the server when using HTTP transport.
-    /// </summary>
-    [JsonPropertyName("port")]
-    public int Port { get; set; }
-
     /// <summary>
     /// Gets or sets the service namespaces to expose through the server.
     /// When null, all available namespaces are exposed.
diff --git a/src/Areas/Server/Options/TransportTypes.cs b/src/Areas/Server/Options/TransportTypes.cs
index e9d1bc218..87d72aac8 100644
--- a/src/Areas/Server/Options/TransportTypes.cs
+++ b/src/Areas/Server/Options/TransportTypes.cs
@@ -12,9 +12,4 @@ internal static class TransportTypes
     /// Standard Input/Output transport mechanism.
     /// </summary>
     public const string StdIo = "stdio";
-
-    /// <summary>
-    /// Server-Sent Events transport mechanism.
-    /// </summary>
-    public const string Sse = "sse";
 }
diff --git a/src/Properties/launchSettings.json b/src/Properties/launchSettings.json
index a90cbe50e..39a163219 100644
--- a/src/Properties/launchSettings.json
+++ b/src/Properties/launchSettings.json
@@ -2,7 +2,7 @@
   "profiles": {
     "azmcp": {
       "commandName": "Project",
-      "commandLineArgs": "server start --transport sse"
+      "commandLineArgs": "server start"
     }
   }
 }
\ No newline at end of file


Test patch:
diff --git a/tests/Areas/Server/UnitTests/Commands/Discovery/RegistryServerProviderTests.cs b/tests/Areas/Server/UnitTests/Commands/Discovery/RegistryServerProviderTests.cs
index 2d8984bf5..543609819 100644
--- a/tests/Areas/Server/UnitTests/Commands/Discovery/RegistryServerProviderTests.cs
+++ b/tests/Areas/Server/UnitTests/Commands/Discovery/RegistryServerProviderTests.cs
@@ -79,7 +79,7 @@ public void CreateMetadata_EmptyDescription_ReturnsEmptyString()
     }
 
     [Fact]
-    public async Task CreateClientAsync_WithUrl_CreatesSseClient()
+    public async Task CreateClientAsync_WithUrl_ThrowsInvalidOperationException()
     {
         // Arrange
         string testId = "sseProvider";
@@ -92,14 +92,11 @@ public async Task CreateClientAsync_WithUrl_CreatesSseClient()
         var provider = new RegistryServerProvider(testId, serverInfo);
 
         // Act & Assert
-        var exception = await Record.ExceptionAsync(() => provider.CreateClientAsync(new McpClientOptions()));
-        Assert.NotNull(exception);
-        // Not an InvalidOperationException about missing URL or invalid transport
-        Assert.IsNotType<InvalidOperationException>(exception);
-        // Should be an HttpRequestException with 404 status
-        Assert.IsType<HttpRequestException>(exception);
-        var httpException = (HttpRequestException)exception;
-        Assert.Equal(HttpStatusCode.NotFound, httpException.StatusCode);
+        var exception = await Assert.ThrowsAsync<InvalidOperationException>(
+            () => provider.CreateClientAsync(new McpClientOptions()));
+
+        Assert.Contains($"Registry server '{testId}' does not have a valid transport type. Only 'stdio' is supported.",
+            exception.Message);
     }
 
     [Fact]
@@ -170,7 +167,7 @@ public async Task CreateClientAsync_NoValidTransport_ThrowsInvalidOperationExcep
         var exception = await Assert.ThrowsAsync<InvalidOperationException>(
             () => provider.CreateClientAsync(new McpClientOptions()));
 
-        Assert.Contains($"Registry server '{testId}' does not have a valid url or type for transport.",
+        Assert.Contains($"Registry server '{testId}' does not have a valid transport type. Only 'stdio' is supported.",
             exception.Message);
     }
 
diff --git a/tests/Areas/Server/UnitTests/Commands/ServiceCollectionExtensionsTests.cs b/tests/Areas/Server/UnitTests/Commands/ServiceCollectionExtensionsTests.cs
index a758707af..a3b138bb1 100644
--- a/tests/Areas/Server/UnitTests/Commands/ServiceCollectionExtensionsTests.cs
+++ b/tests/Areas/Server/UnitTests/Commands/ServiceCollectionExtensionsTests.cs
@@ -21,7 +21,6 @@ public class ServiceCollectionExtensionsTests
 {
     // TransportTypes is internal, so we'll use strings directly
     private const string StdioTransport = "stdio";
-    private const string SseTransport = "sse";
 
     private IServiceCollection SetupBaseServices()
     {
@@ -163,33 +162,7 @@ public void AddAzureMcpServer_WithStdioTransport_ConfiguresStdioTransport()
         Assert.Contains(services, sd => sd.ServiceType == typeof(IMcpServer));
     }
 
-    [Fact]
-    public void AddAzureMcpServer_WithSseTransport_ConfiguresHttpTransport()
-    {
-        // Arrange
-        var services = SetupBaseServices();
-        var options = new ServiceStartOptions
-        {
-            Transport = SseTransport,
-            Port = 8080,
-            // Define proxy as "single" to prevent CompositeDiscoveryStrategy error
-            Mode = "single"
-        };
 
-        // Act
-        services.AddAzureMcpServer(options);
-
-        // Assert
-        // Build the provider to verify that service registration succeeded without exceptions
-        var provider = services.BuildServiceProvider();
-
-        // Check that appropriate registration was completed - we can't directly test
-        // IMcpServer as it requires actual HTTP setup, but we can verify related services
-        Assert.NotNull(provider.GetService<IMcpRuntime>());
-
-        // Verify that the service collection has MCP-related registrations
-        Assert.Contains(services, sd => sd.ServiceType == typeof(IMcpRuntime));
-    }
 
     [Fact]
     public void AddAzureMcpServer_ConfiguresMcpServerOptions()
diff --git a/tests/Areas/Server/UnitTests/ServiceStartCommandTests.cs b/tests/Areas/Server/UnitTests/ServiceStartCommandTests.cs
index 2ae608b64..044a4e6ba 100644
--- a/tests/Areas/Server/UnitTests/ServiceStartCommandTests.cs
+++ b/tests/Areas/Server/UnitTests/ServiceStartCommandTests.cs
@@ -30,9 +30,9 @@ public void Constructor_InitializesCommandCorrectly()
     }
 
     [Theory]
-    [InlineData(null, "", 1234, "stdio")]
-    [InlineData("storage", "storage", 1234, "stdio")]
-    public void ServiceOption_ParsesCorrectly(string? inputService, string expectedService, int expectedPort, string expectedTransport)
+    [InlineData(null, "", "stdio")]
+    [InlineData("storage", "storage", "stdio")]
+    public void ServiceOption_ParsesCorrectly(string? inputService, string expectedService, string expectedTransport)
     {
         // Arrange
         var parseResult = CreateParseResult(inputService);
@@ -40,12 +40,10 @@ public void ServiceOption_ParsesCorrectly(string? inputService, string expectedS
         // Act
         var actualServiceArray = parseResult.GetValueForOption(ServiceOptionDefinitions.Namespace);
         var actualService = (actualServiceArray != null && actualServiceArray.Length > 0) ? actualServiceArray[0] : "";
-        var actualPort = parseResult.GetValueForOption(ServiceOptionDefinitions.Port);
         var actualTransport = parseResult.GetValueForOption(ServiceOptionDefinitions.Transport);
 
         // Assert
         Assert.Equal(expectedService, actualService ?? "");
-        Assert.Equal(expectedPort, actualPort);
         Assert.Equal(expectedTransport, actualTransport);
     }
 
@@ -54,7 +52,6 @@ private static ParseResult CreateParseResult(string? serviceValue)
         var root = new RootCommand
         {
             ServiceOptionDefinitions.Namespace,
-            ServiceOptionDefinitions.Port,
             ServiceOptionDefinitions.Transport
         };
         var args = new List<string>();
@@ -63,9 +60,7 @@ private static ParseResult CreateParseResult(string? serviceValue)
             args.Add("--namespace");
             args.Add(serviceValue);
         }
-        // Add required port/transport defaults for test
-        args.Add("--port");
-        args.Add("1234");
+        // Add required transport default for test
         args.Add("--transport");
         args.Add("stdio");
         return new Parser(root).Parse(args.ToArray());