Skip to content

Commit 07435dc

Browse files
Fix stateless HTTP transport advertising listChanged capability (#1509)
1 parent 6a02c5a commit 07435dc

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

src/ModelContextProtocol.Core/Server/McpServerImpl.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ public McpServerImpl(ITransport transport, McpServerOptions options, ILoggerFact
9898
_notificationHandlers.RegisterRange(notificationHandlers);
9999
}
100100

101+
// In stateless mode, the server cannot send unsolicited notifications,
102+
// so listChanged should not be advertised.
103+
if (transport is StreamableHttpServerTransport { Stateless: true })
104+
{
105+
if (ServerCapabilities.Tools is not null)
106+
ServerCapabilities.Tools.ListChanged = null;
107+
if (ServerCapabilities.Prompts is not null)
108+
ServerCapabilities.Prompts.ListChanged = null;
109+
if (ServerCapabilities.Resources is not null)
110+
ServerCapabilities.Resources.ListChanged = null;
111+
}
112+
101113
// Now that everything has been configured, subscribe to any necessary notifications.
102114
if (transport is not StreamableHttpServerTransport streamableHttpTransport || streamableHttpTransport.Stateless is false)
103115
{

tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,32 @@ public async Task ConfigureSessionOptions_RunsPerRequest_InStatelessMode()
292292
Assert.Equal("configured-beta", Assert.IsType<TextContentBlock>(content2).Text);
293293
}
294294

295+
[Fact]
296+
public async Task StatelessMode_DoesNotAdvertise_ListChangedCapabilities()
297+
{
298+
Builder.Services.AddMcpServer()
299+
.WithHttpTransport(options =>
300+
{
301+
options.Stateless = true;
302+
})
303+
.WithTools([McpServerTool.Create(() => "result", new() { Name = "myTool" })])
304+
.WithPrompts([McpServerPrompt.Create(() => new GetPromptResult(), new() { Name = "myPrompt" })])
305+
.WithResources([McpServerResource.Create(() => new ReadResourceResult(), new() { UriTemplate = "resource://test" })]);
306+
307+
_app = Builder.Build();
308+
_app.MapMcp();
309+
await _app.StartAsync(TestContext.Current.CancellationToken);
310+
311+
HttpClient.DefaultRequestHeaders.Accept.Add(new("application/json"));
312+
HttpClient.DefaultRequestHeaders.Accept.Add(new("text/event-stream"));
313+
314+
await using var client = await ConnectMcpClientAsync();
315+
316+
Assert.Null(client.ServerCapabilities.Tools?.ListChanged);
317+
Assert.Null(client.ServerCapabilities.Prompts?.ListChanged);
318+
Assert.Null(client.ServerCapabilities.Resources?.ListChanged);
319+
}
320+
295321
[McpServerTool(Name = "testSamplingErrors")]
296322
public static async Task<string> TestSamplingErrors(McpServer server)
297323
{

0 commit comments

Comments
 (0)