Inside Model Context Protocol: What MCP Actually Solves
MCP is a JSON-RPC 2.0 transport with a capability handshake. What that buys you, what it does not, and why the N×M integration problem was the real target.
Strip away the branding and Model Context Protocol is JSON-RPC 2.0 with a capability handshake and three noun types. That is not a criticism. The reason MCP got adopted in months rather than years is precisely that it invented almost nothing: it took a 2010-era RPC encoding, defined two transports (stdio and streamable HTTP), and standardized what a tool description looks like on the wire.
The problem it targets is older than LLMs. Call it N×M. You have M client applications that want to call tools, and N tools that want to be callable. Before a shared protocol, every pair needs its own adapter, so you are maintaining M×N integrations. Ten clients and forty tools is four hundred adapters, and each one rots independently. With a protocol in the middle, each client implements the protocol once and each tool implements it once: M+N. Ten plus forty is fifty. That ratio is the entire business case, and it is the same argument that justified ODBC, LSP, and the CUPS printing stack.
The handshake is the interesting part
A session opens with initialize. The client sends its protocol version and the capabilities it supports; the server replies with its own version and declares which of the three primitives it offers. Those primitives are worth naming precisely, because people use them interchangeably and they are not the same thing:
- Tools are model-controlled. The LLM decides to call them. They have side effects.
- Resources are application-controlled. The host app decides what to attach to context. Reading one should be safe and idempotent.
- Prompts are user-controlled. They are templates a human picks from a menu, usually rendered as slash commands.
Getting this wrong is the most common design mistake in a new server. If you expose a read-only lookup as a tool, the model calls it speculatively and burns tokens. If you expose a destructive operation as a resource, some host will fetch it during a context refresh. The distinction is about who holds the trigger, not about whether the operation reads or writes.
What a call actually looks like
After the handshake, the client calls tools/list to get names, descriptions, and JSON Schema for each input. Then an invocation is a plain JSON-RPC request:
--> {
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "recall",
"arguments": { "query": "postgres connection pool sizing", "limit": 3 }
}
}
<-- {
"jsonrpc": "2.0",
"id": 7,
"result": {
"content": [
{ "type": "text", "text": "Pool size set to 20 per instance after the Neon pgbouncer switch (2026-06-02)." }
],
"isError": false
}
}Note isError living inside result rather than using the JSON-RPC errorobject. That is deliberate and it trips people up. A protocol-level error (bad method, malformed params) uses the standard error field and the model never sees it. A tool-level failure (the query returned nothing, the upstream API 500'd) goes in the result so the model can read it and decide what to do. Servers that collapse both into transport errors give the LLM no way to recover. The full details are in the official specification, which is short enough to read end to end in an afternoon.
Four things MCP does not do
It does not define authorization semantics.The HTTP transport binding points at OAuth 2.1 for the connection, and that is where the spec's involvement ends. There is no protocol-level concept of a scope on a tool, no per-tool permission grammar, no notion of "this caller may read but not write." If your server has multi-tenant data, you enforce tenancy yourself, in your handler, on every call. The spec will not catch the bug where a token from user A reaches user B's rows.
It does not give models memory. This gets conflated constantly. MCP standardizes how a model reaches a store. It says nothing about what goes into the store, how entries are ranked, when a stale fact is superseded, or how you keep the index from filling with paraphrases. Those are application problems and they are harder than the transport. A server that faithfully implements the spec and dumps every message into a table has an MCP server and no memory system.
It does not solve tool selection.Whether the model picks the right tool from twenty candidates is a function of your description strings and the model's reasoning, not the protocol. Description quality is load-bearing engineering work. "Search memories" and "Retrieve prior context for this project, including decisions made in earlier sessions on other devices" produce measurably different call rates from the same model.
It does not bound context cost. Nothing stops a tool from returning 40k tokens. Truncation, summarization, and pagination are on you.
MCP is a socket, not a service. It guarantees your server can be reached by any compliant client. Everything about whether reaching it was worthwhile happens on your side of the socket.
Why the transport choice matters more than it looks
Stdio servers are a subprocess of the client. They are trivial to write, they inherit the user's local environment, and they cannot be shared across devices, which is fine for a linter and useless for anything with state. HTTP servers can be reached by a phone, a laptop, and a desktop client at once, but now you own session identity, token rotation, and the fact that a resumed stream may need to replay events the client missed. Most teams start with stdio because it demos fast, then discover the state requirement and rewrite. Pick based on whether two clients will ever need the same data.
Unimatrix runs as an HTTP MCP server for exactly that reason: the point is a store that Claude on a tablet and ChatGPT on a phone both reach, which a subprocess cannot do. The tool surface, including the schemas for remember and recall, is written out in the MCP reference.
Your AI remembers everything. Everywhere.
Unimatrix gives you a shared, durable memory layer across Claude Desktop, Cursor, ChatGPT, and Gemini. Setup in 2 minutes. Free and paid plans available.
Keep reading
Self-consistency samples k reasoning paths and takes the majority answer. It works because errors scatter and correct answers converge, and it costs k times as much.
License terms, context length, and tool-calling reliability matter more than leaderboard rank. A filter for deciding which open weights deserve a GPU-hour.
An MCP client is a non-browser, long-lived consumer. That breaks the assumptions behind short-lived OAuth tokens, and the fix is scoped keys with rotation.