Trefon.
← All notes 9 min

MCP under the hood: JSON-RPC, transports, and capability negotiation

The Model Context Protocol on the wire: the JSON-RPC 2.0 messages, the initialize handshake and capability negotiation, tools, resources and prompts, stdio vs Streamable HTTP, what every tool schema costs in context, and the security model you have to bring yourself.

Jacek Trefon
  • deep dive
  • MCP
  • agents
  • harnesses
  • JSON-RPC
Code ↗

The Model Context Protocol (MCP) is how most agent harnesses now plug in tools they didn’t write. amber speaks it too. Under the marketing it’s a small, readable protocol: JSON-RPC 2.0 over a pipe or HTTP, with a handshake that says who can do what. Here’s what actually goes over the wire.

JSON-RPC 2.0

Every MCP message is a JSON-RPC 2.0 object, one of three kinds:

  • a request, with an id, a method and params, which expects a response;
  • a response, with the same id and either a result or an error;
  • a notification, with a method and no id, which expects no response.

Either side can send requests. The server can ask the client for things too.

The handshake

A session starts with the client announcing its protocol version and capabilities:

{"jsonrpc": "2.0", "id": 1, "method": "initialize",
 "params": {"protocolVersion": "2025-06-18",
            "capabilities": {"roots": {"listChanged": true}, "sampling": {}},
            "clientInfo": {"name": "my-harness", "version": "1.0.0"}}}

The server answers with its own version and capabilities, such as "tools": {"listChanged": true}. The client then sends the notifications/initialized notification, and normal traffic begins.

Capabilities are the contract. A feature not declared in the handshake must not be used, so a client that never offered sampling will never be asked to run a completion for the server.

What servers offer

  • Tools: functions the model can call. tools/list returns each tool’s name, description and inputSchema (JSON Schema). tools/call runs one, and the result is a content array of text, images or embedded resources, with isError for failures. Since the 2025-06-18 revision a tool can also declare an outputSchema and return structuredContent.
  • Resources: data the client can read, identified by URI: files, records, documents. Resources are listed and read (resources/list, resources/read); they’re not executed.
  • Prompts: reusable prompt templates the user can invoke (prompts/list, prompts/get).

And what clients can offer servers, if declared: roots, the filesystem or URI boundaries the server may work within; sampling, which lets the server ask the client’s model for a completion; and elicitation, which lets the server ask the user for input.

When the server’s tool set changes, it sends notifications/tools/list_changed, and the client re-lists.

Transports

  • stdio: the client launches the server as a subprocess, and messages flow as newline-delimited JSON over stdin and stdout. It’s simple, local and the most common way to run a server.
  • Streamable HTTP: for remote servers, one HTTP endpoint. The client POSTs messages, and the server can answer directly or open a Server-Sent Events stream for messages that follow. An Mcp-Session-Id header ties requests into one session. It replaced the earlier HTTP-plus-SSE transport in the 2025-03-26 revision, which also specified OAuth 2.1 for authorisation.

What it costs in context

Every tool’s name, description and schema has to be in the model’s context for the model to use it. Fifty tools at about 200 tokens each is 10K tokens on every request, before the conversation starts. MCP makes it easy to connect many servers and blow that budget without noticing. The fixes are the ones from the context management note:

  • expose only the tools a task needs;
  • keep descriptions short and specific;
  • keep the tool block stable so the prefix cache can reuse it.

The security model you bring yourself

MCP defines the plumbing, not the trust. Three things to treat as hostile until shown otherwise:

  1. Tool descriptions are prompt input. A malicious or compromised server can put instructions in a description, and the model reads it as context. Review the servers you connect like dependencies.
  2. Tool results are untrusted data. A fetched web page or document can carry prompt injection. Don’t let results trigger destructive actions without a check.
  3. Calls have side effects. Writing files, sending messages or running commands should be confirmed or tightly scoped. Roots exist to bound what a server can reach; use them.

Rules I use

  1. Prefer stdio for local tools, and Streamable HTTP with OAuth for remote ones.
  2. Use structured output (outputSchema) where a tool returns data, so results parse without guesswork.
  3. Budget the tool list like any other part of the context.
  4. Gate side effects in the harness, not in the server’s goodwill.

Sources: the Model Context Protocol specification, revisions 2024-11-05, 2025-03-26 and 2025-06-18, and the JSON-RPC 2.0 specification.

Have a problem like this?

Describe your problem

30-minute technical call with the engineer who builds it. No sales team, no slides.