From 9e00ad39cb957e553062870b9f2481b684ad3e46 Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Wed, 1 Jul 2026 18:43:17 +0300 Subject: [PATCH] mcp: avoid panic for new-protocol null IDs Guard the per-request protocol version check so notification-shaped messages with new-protocol metadata do not dereference nil initialize params. This lets malformed id:null calls continue to the existing request validation path instead of crashing. Fixes #1043 --- mcp/server.go | 2 +- mcp/shared_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mcp/server.go b/mcp/server.go index 21b5a722..e19418d5 100644 --- a/mcp/server.go +++ b/mcp/server.go @@ -1812,7 +1812,7 @@ func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any, return nil, perRequestErr } - if validatedMeta.usesNewProtocol && + if validatedMeta.usesNewProtocol && validatedMeta.initializeParams != nil && !slices.Contains(supportedProtocolVersions, validatedMeta.initializeParams.ProtocolVersion) { data, _ := json.Marshal(UnsupportedProtocolVersionData{ Supported: supportedProtocolVersions, diff --git a/mcp/shared_test.go b/mcp/shared_test.go index 08c202a2..41d71ce8 100644 --- a/mcp/shared_test.go +++ b/mcp/shared_test.go @@ -5,12 +5,14 @@ package mcp import ( + "context" "encoding/json" "errors" "strings" "testing" "github.com/google/go-cmp/cmp" + "github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2" "github.com/modelcontextprotocol/go-sdk/jsonrpc" ) @@ -183,6 +185,42 @@ func TestValidateRequestMeta(t *testing.T) { } } +func TestServerHandleNewProtocolNullID(t *testing.T) { + msg, err := jsonrpc.DecodeMessage([]byte(`{ + "jsonrpc": "2.0", + "id": null, + "method": "completion/complete", + "params": { + "_meta": { + "io.modelcontextprotocol/protocolVersion": "2026-07-28", + "io.modelcontextprotocol/clientInfo": {"name": "repro", "version": "0.1.0"}, + "io.modelcontextprotocol/clientCapabilities": {} + }, + "ref": {"type": "ref/prompt", "name": "test"}, + "argument": {"name": "arg", "value": "x"} + } + }`)) + if err != nil { + t.Fatal(err) + } + req, ok := msg.(*jsonrpc.Request) + if !ok { + t.Fatalf("message type = %T, want *jsonrpc.Request", msg) + } + + ss := &ServerSession{server: NewServer(testImpl, nil)} + _, err = ss.handle(context.Background(), req) + if err == nil { + t.Fatal("handle returned nil error, want invalid request") + } + if !errors.Is(err, jsonrpc2.ErrInvalidRequest) { + t.Fatalf("handle error = %v, want invalid request", err) + } + if !strings.Contains(err.Error(), "missing id") { + t.Fatalf("handle error = %v, want missing id", err) + } +} + func TestServerRequest_PerRequestAccessors(t *testing.T) { // A request carrying the new-protocol _meta fields populates the // accessors with values from _meta.