fix(jsonrpc2): decode requests when method key is present (#1000)
## Summary Fixes #976. `DecodeMessage` used `msg.Method != ""` to classify messages. A wire payload with `"method":""` was decoded as a **response**, so stdio servers dropped it without a correlated JSON-RPC error. ## Approach Decode into a `wireDecode` struct whose `method` field is `json.RawMessage`. Treat the message as a **request** when the `method` key is **present on the wire** (including `""`), which matches JSON-RPC semantics. This revision: - Uses a **single JSON parse** per message (dedicated decode struct, no secondary fields-map unmarshal) - Documents why `json.RawMessage` is used for `method` - Adds regression coverage so normal responses and id-only payloads without a `method` key stay on the response path ## Test plan - [x] `go test ./internal/jsonrpc2/... -v` - [x] `go test ./mcp/... -run TestIOConnRead_EmptyMethod -v` - [x] `go test ./... -count=1` - [x] `go test -race ./internal/jsonrpc2/... -count=1` - [x] Server conformance (`npx @modelcontextprotocol/conformance server --url http://127.0.0.1:3000/mcp`) — 40 passed, 0 failed - [x] Client conformance (`./scripts/client-conformance.sh`) — 217 passed, 0 failed ## Scope note This PR addresses the primary repro in #976 (empty `method` with valid `id`). Other cases listed in the issue (`id: null`, structurally invalid envelopes) are unchanged. Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
This commit is contained in:
parent
4dc99b48e1
commit
bcbfe0ce36
1 changed files with 89 additions and 5 deletions
|
|
@ -124,3 +124,26 @@ func TestIOConnRead(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// go-sdk#976: stdio must surface empty-method calls as requests, not responses.
|
||||
func TestIOConnRead_EmptyMethod(t *testing.T) {
|
||||
tr := newIOConn(rwc{
|
||||
rc: io.NopCloser(strings.NewReader(`{"jsonrpc":"2.0","id":5,"method":"","params":{}}`)),
|
||||
})
|
||||
t.Cleanup(func() { tr.Close() })
|
||||
|
||||
msg, err := tr.Read(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("ioConn.Read() error = %v", err)
|
||||
}
|
||||
req, ok := msg.(*jsonrpc.Request)
|
||||
if !ok {
|
||||
t.Fatalf("message type = %T, want *jsonrpc.Request", msg)
|
||||
}
|
||||
if req.Method != "" {
|
||||
t.Errorf("Method = %q, want empty string", req.Method)
|
||||
}
|
||||
if req.ID != jsonrpc2.Int64ID(5) {
|
||||
t.Errorf("ID = %v, want 5", req.ID.Raw())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue