-
Notifications
You must be signed in to change notification settings - Fork 3.6k
First end-to-end 2026-07-28 stateless tools/call (experimental entry + ClientSession pin) #2910
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
c0397c0
Add experimental 2026-07-28 stateless HTTP serving entry
maxisbey 980d57a
Add protocol_version pin to ClientSession for stateless 2026-07-28 mode
maxisbey 188dc83
Pass app and security_settings explicitly to handle_modern_request
maxisbey 9aebd53
Add interaction tests for the 2026-07-28 stateless lifecycle and HTTP…
maxisbey a4f0939
Register 2026-07-28 stateless requirements in the interaction-suite m…
maxisbey cf65e8b
Add MockTransport, capstone, and client-unit tests for the 2026-07-28…
maxisbey 081c564
Register remaining 2026-07-28 stateless requirements
maxisbey ae383c5
Add coverage tests for the experimental modern HTTP entry
maxisbey 26ff922
Reconcile conformance baselines for the stateless serving path
maxisbey 92c078a
Harden the experimental modern HTTP entry's error and cleanup paths
maxisbey 06d1492
Derive transport headers from a constructor protocol_version pin
maxisbey 4378d15
Mark post-shielded-cancel assertions as lax-no-cover for 3.11
maxisbey 194f225
Address review feedback and consolidate 2026-07-28 interaction tests
maxisbey 3afe0f0
Tighten the consolidated 2026-07-28 interaction tests
maxisbey 12f2539
Dispatch ClientSession.protocol_version by era instead of restricting…
maxisbey 4954ed4
Make a pinned ClientSession born-initialized and centralize the moder…
maxisbey 47a422c
Use MODERN_PROTOCOL_VERSIONS membership instead of a threshold constant
maxisbey 7bf87f8
Thread protocol_version through Client and default CacheableResult to…
maxisbey 3fcf832
Turn on the 2026-07-28 column in the interaction-suite matrix
maxisbey 05211a3
Reconcile conformance baselines after CacheableResult default change
maxisbey 1044f42
Report the negotiated protocol_version once initialized; clarify enve…
maxisbey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Pinning
protocol_version="2026-07-28"on aClientconstructed with aServer/MCPServerinstance silently produces a broken session: the pin only flows intostreamable_http_clientfor URL servers, while the in-memory transport still drives the legacy stateful server, so the born-initialized session never sendsinitialize/notifications/initializedand every call fails with an opaque "Invalid request parameters" error. Since the in-memory modern entry is out of scope for this PR, consider a constructor-time fail-fast (raise when aMODERN_PROTOCOL_VERSIONSpin is combined with a non-URL server) or a docstring restriction on the newprotocol_versionfield.Extended reasoning...
What happens
Client.__post_init__(src/mcp/client/client.py:112-118) only forwardsprotocol_versionintostreamable_http_client(...)whenserveris a URL string. Whenserveris aServer/MCPServerinstance it buildsInMemoryTransport(self.server, ...)with no version awareness — but__aenter__still passesprotocol_versionintoClientSession. With a 2026-07-28 pin that session is "born initialized":initialize()returns the locally-synthesized result without ever touching the wire, and nonotifications/initializedis sent.Why every call then fails
InMemoryTransport._connectruns the lowlevel server viaactual_server.run(...)with the defaultstateless=False, so the legacy init gate inServerRunner._on_requestapplies: any non-pingrequest beforeinitialize/notifications/initializedis rejected withMCPError(INVALID_PARAMS, "Invalid request parameters"). The per-requestio.modelcontextprotocol/protocolVersionenvelope the pinned session stamps does not help —2026-07-28is not inSUPPORTED_PROTOCOL_VERSIONS, and the init gate is independent of version resolution anyway.Step-by-step proof
server = MCPServer("test")with anaddtool (the exact pattern in theClientclass docstring example).async with Client(server, protocol_version="2026-07-28") as client:—__post_init__wrapsserverinInMemoryTransport(no version pin);__aenter__builds a stateless-pinnedClientSessionand callsinitialize(), which returns the synthesized result without sending any frame. The context manager enters successfully.await client.call_tool("add", {"a": 1, "b": 2})— the request reaches the lowlevel server'sServerRunner._on_request;connection.initialize_acceptedis false (no handshake ever happened), so the gate raisesINVALID_PARAMSand the user seesMCPError: Invalid request parameters— an error that points nowhere near the actual cause (an unsupported transport/version combination chosen at construction).Why nothing prevents it today
Nothing in
Client.__post_init__rejects or warns about the combination, and the newClient.protocol_versiondocstring describes the stateless behaviour generically ("Pinning to 2026-07-28 or later selects the stateless transport era...") with HTTP only mentioned as an additional detail — while the class's own primary docstring example is exactly the in-memoryMCPServercase. The PR description lists the in-memory modern entry as out of scope, and the interaction suite era-locksin-memoryto 2025-11-25 inTRANSPORT_SPEC_VERSIONS, so the gap is known internally — but the public API surface gives the user no signal.Impact and suggested fix
This is not silent corruption — the first call fails loudly — but the failure is confusing and far removed from the cause, on a brand-new public parameter whose docstring invites exactly this usage. A cheap, non-breaking guard fixes it: in
__post_init__, raise (e.g.ValueError) whenprotocol_version in MODERN_PROTOCOL_VERSIONSandserveris aServer/MCPServerinstance (or, more conservatively, any non-URL transport), with a message pointing at the missing in-memory modern entry. Alternatively, restrict the pin to URL/HTTP servers in theprotocol_versiondocstring until the in-memory modern entry lands.