From 07435dcb161b828939b0424707428ac2b35baeca Mon Sep 17 00:00:00 2001 From: Jayaraman Venkatesan <112980436+jayaraman-venkatesan@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:30:12 -0400 Subject: [PATCH] Fix stateless HTTP transport advertising listChanged capability (#1509) --- .../Server/McpServerImpl.cs | 12 +++++++++ .../StatelessServerTests.cs | 26 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs index 50c988c..04d11e0 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs @@ -98,6 +98,18 @@ internal sealed partial class McpServerImpl : McpServer _notificationHandlers.RegisterRange(notificationHandlers); } + // In stateless mode, the server cannot send unsolicited notifications, + // so listChanged should not be advertised. + if (transport is StreamableHttpServerTransport { Stateless: true }) + { + if (ServerCapabilities.Tools is not null) + ServerCapabilities.Tools.ListChanged = null; + if (ServerCapabilities.Prompts is not null) + ServerCapabilities.Prompts.ListChanged = null; + if (ServerCapabilities.Resources is not null) + ServerCapabilities.Resources.ListChanged = null; + } + // Now that everything has been configured, subscribe to any necessary notifications. if (transport is not StreamableHttpServerTransport streamableHttpTransport || streamableHttpTransport.Stateless is false) { diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs index 131adcd..80c37ea 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs @@ -292,6 +292,32 @@ public class StatelessServerTests(ITestOutputHelper outputHelper) : KestrelInMem Assert.Equal("configured-beta", Assert.IsType(content2).Text); } + [Fact] + public async Task StatelessMode_DoesNotAdvertise_ListChangedCapabilities() + { + Builder.Services.AddMcpServer() + .WithHttpTransport(options => + { + options.Stateless = true; + }) + .WithTools([McpServerTool.Create(() => "result", new() { Name = "myTool" })]) + .WithPrompts([McpServerPrompt.Create(() => new GetPromptResult(), new() { Name = "myPrompt" })]) + .WithResources([McpServerResource.Create(() => new ReadResourceResult(), new() { UriTemplate = "resource://test" })]); + + _app = Builder.Build(); + _app.MapMcp(); + await _app.StartAsync(TestContext.Current.CancellationToken); + + HttpClient.DefaultRequestHeaders.Accept.Add(new("application/json")); + HttpClient.DefaultRequestHeaders.Accept.Add(new("text/event-stream")); + + await using var client = await ConnectMcpClientAsync(); + + Assert.Null(client.ServerCapabilities.Tools?.ListChanged); + Assert.Null(client.ServerCapabilities.Prompts?.ListChanged); + Assert.Null(client.ServerCapabilities.Resources?.ListChanged); + } + [McpServerTool(Name = "testSamplingErrors")] public static async Task TestSamplingErrors(McpServer server) {