diff --git a/mcp/client.go b/mcp/client.go index 74037990..fff196f3 100644 --- a/mcp/client.go +++ b/mcp/client.go @@ -383,7 +383,10 @@ func (c *Client) Connect(ctx context.Context, t Transport, opts *ClientSessionOp Capabilities: c.capabilities(protocolVersion), } req := &InitializeRequest{Session: cs, Params: params} - res, err := handleSend[*InitializeResult](ctx, methodInitialize, req) + // The header must agree with params.protocolVersion: no version has been + // negotiated yet, so nothing else identifies what this request proposes. + initializeCtx := context.WithValue(ctx, protocolVersionContextKey{}, protocolVersion) + res, err := handleSend[*InitializeResult](initializeCtx, methodInitialize, req) if err != nil { _ = cs.Close() return nil, err diff --git a/mcp/streamable_client_test.go b/mcp/streamable_client_test.go index a5957bf6..9c519b7f 100644 --- a/mcp/streamable_client_test.go +++ b/mcp/streamable_client_test.go @@ -145,6 +145,18 @@ func (s *fakeStreamableServer) ServeHTTP(w http.ResponseWriter, req *http.Reques if v := req.Header.Get(protocolVersionHeader); v != resp.wantProtocolVersion && resp.wantProtocolVersion != "" { s.t.Errorf("%v: bad protocol version header: got %q, want %q", key, v, resp.wantProtocolVersion) } + // On initialize no version has been negotiated yet, so the header carries + // what the request proposes and must agree with the body. Checked for every + // initialize rather than per-response, since it holds unconditionally. + if key.jsonrpcMethod == methodInitialize && jsonrpcReq != nil { + var params InitializeParams + if err := json.Unmarshal(jsonrpcReq.Params, ¶ms); err != nil { + s.t.Errorf("%v: unmarshal initialize params: %v", key, err) + } else if got := req.Header.Get(protocolVersionHeader); got != params.ProtocolVersion { + s.t.Errorf("%v: protocol version header %q disagrees with body protocolVersion %q", + key, got, params.ProtocolVersion) + } + } w.Write([]byte(body)) rc.Flush() // flush response