modelcontextprotocol / modelcontextprotocol/rust-sdk
SEP-2260 follow-up: skip session tracking and the standalone GET stream at protocol >= 2026-07-28
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 3.9k
- Forks
- 645
- Avg merge
- 4d 13h
- Merged PRs (30d)
- 36
Description
Follow-up to #1033 / PR #1055.
PR #1055 makes the client reject SEP-2260-restricted requests that arrive on the standalone GET stream: the streamable HTTP transport marks inbound requests with an InboundStreamOrigin, the service layer turns that into a PeerRequestAssociation, and RoleClient::enforce_peer_request_association answers -32602 for Unassociated.
Rejecting is the right behavior for a server that sends such a request. But at protocol 2026-07-28 the client shouldn't have the stream in the first place. SEP-2567 removes sessions and the standalone GET endpoint at that version, so an Mcp-Session-Id and a GET stream are both artifacts of a pre-2026-07-28 server shape. Keeping them open at 2026-07-28 means maintaining an inbound path whose only purpose is to be refused.
The modern path already gets this right. A server/discover startup sets session_id to None unconditionally (streamable_http_client.rs, ~L892), and there's a debug_assert! that discover bootstrap creates no session state (~L1054). What's left is the legacy-shaped paths, where a session id survives regardless of the version that was ultimately negotiated. spawn_common_stream has three call sites and all three are guarded only by if let Some(session_id), never by negotiated_version:
- Legacy startup (~L969). A server that answers legacy
initializewith both a session id andprotocolVersion: 2026-07-28gets a GET stream. This is the shape the e2e test in #1055 scripts deliberately. - Fallback
initializeafterserver/discoverfails (~L1382, on theinitializednotification). Same shape, reached throughClientLifecycleMode::Auto. - Session re-establishment after an expired-session 404 (~L1218). Re-spawns on the new session id without reconsulting the version.
Work: gate session tracking and spawn_common_stream on negotiated_version < ProtocolVersion::STANDARD_HEADERS, so that at 2026-07-28 and later no session id is retained and no GET stream is opened. PeerRequestAssociation::Unassociated then becomes unreachable over streamable HTTP rather than merely rejected, and the enforcement from #1055 stays as the defense for anything that still slips through.
Two open questions worth settling in the same change:
- What to do when a server returns a session id and negotiates
2026-07-28. Dropping the id silently is the least disruptive, but the server plainly expects it echoed back, so failing startup may be more honest than half-speaking its dialect. - Whether session cleanup (
SessionCleanupInfo, theDELETEon shutdown) should also be skipped, or kept so a session the server did create still gets torn down.
This also bears on #863 (expose the negotiated Mcp-Session-Id): if there is deliberately no session at 2026-07-28, the accessor is a legacy-only affordance, and that's probably worth saying there either way.
Not a conformance gap — the receive-side check is a SHOULD and #1055 covers the scenarios — so this is hardening rather than a fix.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in streamable_http_client.rs at the legacy startup (~L969), session re-establishment (~L1218), fallback initialize (~L1382), and the three spawn_common_stream call sites. Review the modern server/discover path around ~L892 and ~L1054, then use the e2e scenario from #1055 as a reference. Done means modern negotiated versions retain no session id and open no GET stream, with session cleanup and startup behavior decided.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100