modelcontextprotocol / modelcontextprotocol/go-sdk
mcp: the negotiated protocol version is recorded on one path of four, so stdio accepts batches a SEP-2575 session forbids
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 5.1k
- Forks
- 543
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 37
Description
Part of the audit indexed at #1257.
Four places record a protocol version on a ServerSession and only one of them records it in NegotiatedProtocolVersion:
ServerSession.initializerecords both, the negotiated one throughnegotiatedVersion.Server.handlerecordsInitializeParamsfrom the version a new-protocol client declares in_meta, once its support check has accepted it.server/discoverrecordsInitializeParamswith the version it was asked about.- The streamable handler synthesizes
InitializeParamsfrom theMCP-Protocol-Versionheader for an old-protocol request with no handshake.
Readers that ask what a session speaks therefore disagree depending on how the session began. The concrete failure is on stdio: ioConn.sessionUpdated reads only NegotiatedProtocolVersion, so a SEP-2575 session over stdio is treated as 2025-03-26 and accepts JSON-RPC batches. Batching was removed in 2025-06-18, and the streamable handler already refuses them by reading the header, so the two transports disagree about the same session shape.
The support check each of the other three paths applies is all the negotiation SEP-2575 has: there is no handshake response in which a downgrade could be communicated, so a version those paths accept is one the server supports, and recording it as negotiated states what happened. initialize stays the one path that downgrades, because the lifecycle page requires it to answer with a version it supports and InitializeResult.ProtocolVersion is where the client learns the result.
The fallback in the unexported protocolVersion() accessor stays. ServerSessionOptions.State is exported, so a caller may supply InitializeParams with no negotiated version, and state persisted before #1199 carries none either.
Raised by the maintainer during review of #1266: "what do you think about setting the ss.state.NegotiatedProtocolVersion also in case of new protocol version?" The pull request is linked below.
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 by tracing protocol-version assignment in ServerSession.initialize, Server.handle, server/discover, and the streamable handler, then inspect ioConn.sessionUpdated and the unexported protocolVersion() accessor. Verify that every accepted non-downgrade path records the negotiated version while preserving the fallback for state without one, and test that stdio rejects batches for SEP-2575 sessions as the streamable handler does.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend-api-design
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 64/100