diff --git a/mcp/protocol.go b/mcp/protocol.go index 8ca96b24..d78d2506 100644 --- a/mcp/protocol.go +++ b/mcp/protocol.go @@ -128,9 +128,12 @@ func (m *InputRequestMap) UnmarshalJSON(data []byte) error { } result[k] = &p case methodListRoots: + // For a roots/list request, params are optional. var p ListRootsParams - if err := internaljson.Unmarshal(raw.Params, &p); err != nil { - return err + if len(raw.Params) > 0 { + if err := internaljson.Unmarshal(raw.Params, &p); err != nil { + return err + } } result[k] = &p default: diff --git a/mcp/protocol_test.go b/mcp/protocol_test.go index cd2ae94e..86f869e8 100644 --- a/mcp/protocol_test.go +++ b/mcp/protocol_test.go @@ -1222,6 +1222,21 @@ func TestInputRequestMapJSON(t *testing.T) { t.Error("Unmarshal succeeded, want an error for a null input request") } }) + + // roots/list params are optional, so a request without them must not fail + // decoding the whole result. + for _, params := range []string{``, `,"params":null`} { + t.Run("roots/list without params"+params, func(t *testing.T) { + var got CallToolResult + data := `{"content":[],"inputRequests":{"r1":{"method":"roots/list"` + params + `}}}` + if err := json.Unmarshal([]byte(data), &got); err != nil { + t.Fatalf("Unmarshal(%s) = %v, want nil", data, err) + } + if p, ok := got.InputRequests["r1"].(*ListRootsParams); !ok || p == nil { + t.Errorf("InputRequests[r1] = %#v, want non-nil *ListRootsParams", got.InputRequests["r1"]) + } + }) + } } func TestInputResponseMapJSON(t *testing.T) {