Loomal

JSON-RPC

JSON-RPC is a lightweight remote procedure call protocol encoded in JSON, and the wire format underlying all Model Context Protocol communication.

Also known as: JSON-RPC 2.0

What is JSON-RPC?

JSON-RPC is a minimal protocol for calling procedures on another process: you send a JSON object naming a method and its parameters, and you get a JSON object back with the result. The current version, JSON-RPC 2.0, was finalized in 2010 and has barely changed since — it is deliberately small.

Unlike REST, JSON-RPC doesn't care about URLs, verbs, or resources. Unlike gRPC, it needs no schema compiler or binary encoding. It is just structured JSON over whatever channel you have.

Anatomy of a JSON-RPC message

A request has four fields: "jsonrpc" (always "2.0"), "method" (the procedure name, e.g. tools/call), "params" (the arguments), and "id" (so responses can be matched to requests when several are in flight). The response carries the same id plus either a "result" or an "error" object.

A request sent without an id is a notification — fire-and-forget, no response expected. MCP uses notifications for things like progress updates and change events, where an acknowledgment would just be noise.

Why MCP is built on JSON-RPC

Every exchange in the Model Context Protocol — listing tools, calling a tool, reading a resource, fetching a prompt — is a JSON-RPC 2.0 message. The method namespaces them: tools/list, tools/call, resources/read, prompts/get.

The choice was pragmatic. JSON-RPC is transport-agnostic, which lets the identical message format run over a local stdio pipe between a client and a subprocess, over Server-Sent Events, or over Streamable HTTP to a remote server. Server authors write one protocol implementation regardless of how clients connect.

JSON-RPC and transports

JSON-RPC defines what messages look like, not how they travel. In MCP, stdio transport frames messages as newline-delimited JSON on a subprocess's standard input and output; Streamable HTTP posts them to a single endpoint and streams responses back; the older SSE transport used a separate event stream for server-to-client traffic.

This separation is why a server can migrate from local stdio distribution to remote hosting without touching its tool logic — only the transport layer changes.

Reading JSON-RPC when debugging

Because the format is plain JSON, MCP traffic is easy to inspect by eye. If a tool call fails, the error object's code and message usually point straight at the problem — a -32601 means the method doesn't exist, a -32602 means your params didn't match the tool's schema. Tools like MCP Inspector are essentially JSON-RPC viewers with an MCP-aware UI on top.