Skip to content

mcp: clear the caller's protocol version for the initialize handshake - #1165

Merged
guglielmo-san merged 2 commits into
modelcontextprotocol:mainfrom
gwyszynski-sa:fix/initialize-protocol-version-header
Oct 27, 2026
Merged

guglielmo-san merged 2 commits into
modelcontextprotocol:mainfrom
gwyszynski-sa:fix/initialize-protocol-version-header

Conversation

@gwyszynski-sa

@gwyszynski-sa gwyszynski-sa commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

The legacy initialize handshake pins params.protocolVersion but sends no
Mcp-Protocol-Version header of its own: setMCPHeaders finds c.initializedResult
still nil, since it is assigned a few lines later, and the message carries no
_meta.protocolVersion, so the header falls to the request context. For a
standalone client that context is empty and the header is absent; for a process
that is both a server and a client it holds that process's inbound version,
which contradicts the body it accompanies. This is the client-side mirror of
#963.

No version is negotiated at that point, so the request has none of its own to
state, and the caller's must not stand in for one. Clears it for the initialize
send rather than sending the proposed version: a header the peer does not
support fails the handshake outright, where the body would have negotiated down.
Standalone clients are unaffected, the header having already been absent there.

The fake server asserted wantProtocolVersion for other methods but for none of
its sixteen initialize cases. Asserts centrally that initialize carries no
protocol version header, and adds a Connect whose context already holds one,
without which that assertion cannot fail. Requests after negotiation are left
unchecked there, since which source wins once initializedResult is set is #1162.

Fixes #1164

Comment thread mcp/client.go Outdated
The legacy initialize handshake pins params.protocolVersion but sends no
Mcp-Protocol-Version header of its own: setMCPHeaders finds c.initializedResult
still nil, since it is assigned a few lines later, and the message carries no
_meta.protocolVersion, so the header falls to the request context. For a
standalone client that context is empty and the header is absent; for a process
that is both a server and a client it holds that process's inbound version,
which contradicts the body it accompanies. This is the client-side mirror of
modelcontextprotocol#963.

No version is negotiated at that point, so the request has none of its own to
state, and the caller's must not stand in for one. Clears it for the initialize
send rather than sending the proposed version: a header the peer does not
support fails the handshake outright, where the body would have negotiated down.
Standalone clients are unaffected, the header having already been absent there.

The fake server asserted wantProtocolVersion for other methods but for none of
its sixteen initialize cases. Asserts centrally that initialize carries no
protocol version header, and adds a Connect whose context already holds one,
without which that assertion cannot fail. Requests after negotiation are left
unchecked there, since which source wins once initializedResult is set is modelcontextprotocol#1162.

Fixes modelcontextprotocol#1164
@gwyszynski-sa gwyszynski-sa changed the title mcp: send the proposed protocol version in the initialize header mcp: clear the caller's protocol version for the initialize handshake Aug 24, 2026
@gwyszynski-sa
gwyszynski-sa force-pushed the fix/initialize-protocol-version-header branch from babfbb3 to 80f70c5 Compare August 24, 2026 19:51
@guglielmo-san
guglielmo-san merged commit 3a46369 into modelcontextprotocol:main Oct 27, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

initialize sends no Mcp-Protocol-Version of its own, so the header can contradict params.protocolVersion

2 participants