Connect by JBRH Open Connect

JSON-RPC 2.0

JSON-RPC 2.0 is a small convention for calling a method by name with JSON arguments and getting a JSON result back. It defines four message shapes and a set of reserved error codes, and nothing else. Both of Connect's agent endpoints — /mcp and /a2a — speak it.

Status
Reference What this means
Audience
developer
Last verified
Product version
6.3.2

Four shapes, and that is the whole protocol#

  • Requestjsonrpc, method, optional params, and an id the caller chooses. The id is how a reply is matched to its question.
  • Response (success)jsonrpc, the same id, and result.
  • Response (error)jsonrpc, the same id, and error with a code, a message and optional data.
  • Notification — a request with no id. No reply is sent, ever. A caller that waits for one waits forever.

params may be an object or an array in the specification. Both of Connect's endpoints require an object and answer -32602 for anything else, because named parameters survive a schema change and positional ones do not.

What JSON-RPC deliberately leaves out is as important as what it defines: no transport, no authentication, no discovery, no schema for params, no versioning of methods. Everything a real protocol needs is layered on top, which is why MCP and A2A can share this envelope and still be different protocols.

The reserved error codes#

CodeNameWhen you see it
-32700Parse errorThe body was not valid JSON
-32600Invalid requestValid JSON, wrong shape — a batch where one message was expected
-32601Method not foundThe method name is not implemented
-32602Invalid paramsThe method exists; the arguments are wrong
-32603Internal errorThe server failed for its own reasons
-32000 to -32099Server-definedReserved for the layer above: MCP's -32020, A2A's -32004

The server-defined band is where the protocols above put their own meanings. MCP uses -32020 when the MCP-Protocol-Version, Mcp-Method or Mcp-Name header disagrees with the body. A2A uses -32004 for an operation it does not support — a skill that is not on the card, streaming when the card declares none, or the authenticated extended card that does not exist.

One distinction is worth internalising because it decides how a client should react. A JSON-RPC error means the *call* did not happen. A successful result that reports a problem — MCP's isError: true — means the call happened and the answer is bad news. The first is retried or fixed; the second is read.

Does Connect use JSON-RPC?#

Yes, on both agent endpoints, and nowhere else. POST /mcp and POST /a2a are JSON-RPC. The public HTTP API is ordinary REST over JSON and is described by OpenAPI — it is not JSON-RPC and does not use these codes.

Two deliberate narrowings apply to both endpoints. Batching is refused: send one message per request, and a JSON array comes back as -32600 with 400. And params must be an object.

Connect also maps codes onto HTTP status rather than answering 200 for everything, because the transport in front of an endpoint — a proxy, a gateway, a metrics collector — can only see the status line.

SituationHTTPJSON-RPC code
Malformed JSON400-32700
A batch, or the wrong top-level shape400-32600
Header disagrees with the body (MCP)400-32020
Unknown method404-32601
Unknown tool, resource or prompt200-32601
Bad arguments200-32602
Body over the size ceiling413-32600
Rate limit reached429-32603
A notification202no body at all

The split on -32601 is intentional. An unknown *method* is a routing failure a proxy should be able to see, so it gets 404. An unknown tool name is an application-level answer to a well-formed call, so it stays 200 and the model reads the message.

Writing a client that does not surprise you#

  1. Generate a fresh id per request and match replies by it. Reusing one across concurrent calls makes two answers indistinguishable.
  2. Treat a missing id as intentional. If you send a notification, do not wait; if you receive 202, that is the whole answer.
  3. Read error.data when it is present — Connect uses it to list the supported protocol versions when a client asks for one that is not.
  4. Do not parse error.message for meaning. The code is the contract; the message is for a human reading a log.
  5. Retry on 429 with a backoff, and on -32603 at most once. Never retry -32602: the arguments will be just as wrong the second time.

Questions#

Why does Connect refuse JSON-RPC batches when the specification allows them?

Because a batch makes every limit ambiguous — rate, body size, partial failure — for a saving that matters only to chatty clients. One message per request keeps error handling honest, and neither MCP nor A2A needs batching to work.

Is the public API JSON-RPC too?

No. The public API is REST over JSON with ordinary HTTP status codes and its own error shape. JSON-RPC here is confined to the two agent endpoints.

What does a code in the -32000 range mean?

It is defined by the protocol above JSON-RPC, not by JSON-RPC. Look it up in MCP's or A2A's specification — here, -32020 is an MCP header mismatch and -32004 is an A2A unsupported operation.