Fix stateless HTTP transport advertising listChanged capability (#1486)#1509
Merged
halter73 merged 1 commit intomodelcontextprotocol:mainfrom Apr 9, 2026
Conversation
mikekistler
approved these changes
Apr 9, 2026
Contributor
mikekistler
left a comment
There was a problem hiding this comment.
Looks good! 👍
Thanks for this contribution!
halter73
approved these changes
Apr 9, 2026
Contributor
|
Thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #1486
When configuring an MCP server with
Stateless = trueon the HTTP transport, theinitializeresponse incorrectly advertisedlistChanged: truefor tools, resources, and prompts. In stateless mode there is no persistent session or SSE connection, so the server can never send unsolicited notifications — advertising this capability is misleading.Before:
After:
The capability objects remain — the server still supports tools/prompts/resources — only the
listChangedkey is suppressed.Root Cause
In
McpServerImpl.cs, theConfigureTools,ConfigurePrompts, andConfigureResourcesmethods each setlistChanged = trueunconditionally whenever a primitive collection is registered, regardless of transport mode. The existing stateless guard at line ~102 already skips wiring up notification event handlers, but it never corrected the capability flag that had already been set.Changes
src/ModelContextProtocol.Core/Server/McpServerImpl.csAdded a block in the constructor immediately before the existing stateless notification-registration guard that nulls out
ListChangedon each capability when the transport is stateless:Setting to
null(notfalse) omits the key entirely from the serialized JSON since the SDK usesDefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, producing"tools": {}rather than"tools": { "listChanged": false }.The fix is placed in the constructor rather than inside each individual
Configure*method to keep all stateless-related behaviour co-located in one block, alongside the existing notification handler suppression, making it easy to see all stateless implications in one place.tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.csAdded
StatelessMode_DoesNotAdvertise_ListChangedCapabilities— starts a stateless server configured with tools, prompts, and resources, connects a real in-process client viaKestrelInMemoryTest, and asserts that all threeListChangedcapability flags arenullin theinitializeresponse.Verification
Verified with a AI generated sample app that sends a raw
initializerequest and performs a live tool-mutation test to confirm the end-to-end behaviour:https://github.com/jayaraman-venkatesan/stateless-verify
Section 1 — initialize response:
"tools": { "listChanged": true }"tools": {}"tools": { "listChanged": true }"tools": { "listChanged": true }Section 2 — live notification test:
toolCollection.Add(...)toolCollection.Add(...)Section 2 confirms why the capability must not be advertised in stateless mode: even if the server attempted to send the notification, there is no channel to deliver it.
Checklist
listChanged = truewas set unconditionally inConfigureTools,ConfigurePrompts, andConfigureResourcesregardless of transport modelistChanged: trueis still advertised whenStateless = falsenullproduces"tools": {}, not"tools": { "listChanged": false }and not a missingtoolskeyStatelessServerTests.cs— correct home alongside all other stateless-specific testsdotnet build ModelContextProtocol.slnx— cleandotnet test tests/ModelContextProtocol.AspNetCore.Tests/ --framework net10.0— 309 passed, 0 failed