modelcontextprotocol / modelcontextprotocol/go-sdk
initialize sends no Mcp-Protocol-Version of its own, so the header can contradict params.protocolVersion
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 5.1k
- Forks
- 543
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 37
Description
Describe the bug
The legacy initialize handshake pins the version it proposes into the body but sends no Mcp-Protocol-Version header of its own (client.go#L376-L386):
// Use the latest legacy protocol version for the fallback initialize.
protocolVersion = protocolVersion20251125
...
params := &InitializeParams{ProtocolVersion: protocolVersion, ...}
req := &InitializeRequest{Session: cs, Params: params}
res, err := handleSend[*InitializeResult](ctx, methodInitialize, req)
handleSend gets the plain ctx, not the discoverCtx used for the probe above it. By the time setMCPHeaders runs, c.initializedResult is still nil — it is assigned about six lines later — and the message carries no _meta.protocolVersion, so the header is decided by whatever the request context happens to hold.
For a standalone client that context is empty and the header is absent. For a process that is both an MCP server and an MCP client, the server transport has recorded its inbound request's version on that context (streamable.go#L375) and that value goes out instead, contradicting the body it accompanies.
To Reproduce
Steps to reproduce the behavior:
- Run a Go SDK Streamable HTTP client against a server that does not implement
server/discover, so it falls back to the legacyinitialize. - Observe the header on that
initializerequest: absent for a standalone client, or the inbound version if the caller's context carriesprotocolVersionContextKey.
The same disagreement can be produced directly against a Go SDK server, as in #963:
curl -i -sS -X POST "$ENDPOINT" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2026-07-28' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"repro","version":"0.1.0"}}}'
What did you see
initialize body="2025-11-25" header="" (standalone client)
initialize body="2025-11-25" header="2026-07-28" (server+client process)
What did you expect to see
The header stating the version the request proposes, matching params.protocolVersion, and never inherited from ambient context:
initialize body="2025-11-25" header="2025-11-25"
Omitting the header is the other reasonable option, and is what the package doc for the header currently describes ("Is omitted before initialization completes", streamable_client.go). Sending it seems preferable: it agrees with the body, it is what a server validating the header expects, and it removes the dependency on the context being empty rather than relying on it. Happy to switch the fix to omission if maintainers prefer that reading.
Logs
An upstream that validates the header rejects the handshake outright:
400 Bad Request: Unsupported protocol version: 2026-07-28 (supported versions: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07)
What version of the Go MCP SDK are you using
v1.7.0, and main at 64e454e.
What version of Go are you using
go version go1.26.5 darwin/arm64. Not Go-version dependent.
Additional context
This is the client-side mirror of #963, which tightened the server against exactly this mismatch — the SDK's server now rejects a disagreement its own client can still produce.
Separate from #1162: fixing the precedence there does not fix this, because initializedResult is nil at this point regardless of ordering, so control still reaches the context branch.
The fake server in streamable_client_test.go asserts wantProtocolVersion for other methods but for none of its sixteen initialize cases, so neither an absent nor a contradicting header is currently covered.
I have a fix and have opened #1165.
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 mcp/client.go#L376-L386 and trace setMCPHeaders, then inspect the protocol-version handling in mcp/streamable.go#L375. Add coverage in mcp/streamable_client_test.go for the legacy initialize cases; done means the initialize header is either absent by the documented behavior or matches params.protocolVersion without inheriting ambient context.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, networking
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100