What is JSONRPCResponse?
JSONRPCResponse is the success-path envelope in the JSON-RPC 2.0 protocol layer that MCP builds on. Every request a client sends to a server (or vice versa for sampling) that expects a reply must receive exactly one response: either a JSONRPCResponse carrying the operation's result, or a JSONRPCError carrying a structured failure. The two are mutually exclusive — you never combine both in a single reply, and you never omit a reply entirely for a request (as opposed to a notification, which gets none). The wire shape is intentionally minimal: a fixed jsonrpc field pinned to the string "2.0", an id that mirrors the originating request (either a string or a number, never null for responses), and a result field that holds an arbitrary JSON object defined by the specific MCP method. The id is the correlation handle — receivers match the response to the in-flight request using it, which is what enables MCP to multiplex many concurrent calls over a single transport connection without blocking. From an evolution standpoint, MCP inherits JSON-RPC 2.0's spec verbatim here and has not extended the envelope itself. Method-specific schemas live entirely inside result, so the protocol can add new capabilities by defining new result shapes without touching JSONRPCResponse. This separation keeps generic transport plumbing — routing, timeouts, retries — independent of application-layer semantics.
When to use
After every successful method call.
When NOT to use
If the method errored — use JSONRPCError.
Notes
id Must Exactly Mirror the Request
The id field must be the same type and value as the originating request's id — do not coerce a numeric id to a string or vice versa. Some JSON parsers silently convert large integers to floats, which can corrupt the correlation if the client used a large numeric id. Prefer string ids (e.g., UUIDs) in production SDKs to sidestep this entirely.
result Is Always an Object
JSON-RPC 2.0 allows result to be any JSON value, but MCP constrains it to an object for every method. Passing a bare string, array, or null as result is a protocol violation and will cause strict validators to reject the response. Even methods that return nothing (e.g., notifications/initialized) use an empty object rather than null.
One Response Per Request, No Exceptions
Sending two responses for the same id — even if the first was an error — is undefined behavior and will confuse any multiplexed transport layer. If your handler encounters a secondary failure after already dispatching a JSONRPCError, log it locally rather than emitting a second wire message. Likewise, never send a JSONRPCResponse for a notification (a message with no id), as notifications explicitly opt out of the request-response cycle.
Large result Payloads and Backpressure
MCP imposes no hard size limit on result, but streaming transports (SSE, WebSocket) can stall or drop messages when result objects grow into the megabyte range — common with tools that return large file contents or embeddings. Consider pagination (cursor-based) or out-of-band resource references rather than embedding large blobs directly in the response body.
Distinguishing JSONRPCResponse from JSONRPCError
At the type-discriminator level, a frame is a JSONRPCResponse if it has a result key, and a JSONRPCError if it has an error key. Both share jsonrpc and id. Deserializers should check for the presence of error first, since a malformed server might accidentally include both keys — treating error as authoritative matches the JSON-RPC 2.0 spec intent and avoids silently swallowing failures.
Fields
| Field | Type | Required | Purpose |
|---|---|---|---|
| jsonrpc | '2.0' | yes | Protocol identifier. |
| id | string | number | yes | Must match request id. |
| result | object | yes | Method-specific result payload. |
Examples
Ping reply
{ "jsonrpc": "2.0", "id": 1, "result": {} }
Common mistakes
❌ Including both result and error
✅ Exactly one of result/error per response.