Research · 2026-05-20

MCP without the fog: a working mental model

protocolmcp-fsmcpg

The explainer the lab wished existed: MCP as a JSON-RPC data layer cleanly separated from its transports, and why HTTP verbs are not MCP methods.

Most MCP confusion comes from conflating two layers the spec keeps separate. This note is the lab's canonical untangling, kept current against the official specification.

The data layer is JSON-RPC 2.0. Everything meaningful in MCP — initialize, tools/list, tools/call, resource reads, subscriptions, notifications — is a JSON-RPC message. This layer defines the protocol's semantics, its lifecycle, and its capability negotiation. If you can read a JSON-RPC exchange, you can read MCP.

Transports just move bytes. stdio pipes messages between parent and child; Streamable HTTP moves the same messages over POST bodies and (optionally) SSE streams. The HTTP verbs are plumbing: a GET opens a listening stream, a POST delivers messages, a DELETE ends a session. None of them is an MCP operation — the operation is always inside the JSON. Most integration bugs the lab has debugged trace back to exactly this category error.

The architecture is host/client/server. A host application owns one or more clients; each client speaks to exactly one server; servers export tools, resources, and prompts. Capabilities are negotiated at initialize time, and nothing not negotiated may be used — which is why version and capability handling is where conformance suites earn their keep.

The note exists because the lab implements MCP at every altitude — a three-tool reference server (mcp-fs), a governing gateway (MCPG, 100% MUST coverage), CLI projections (mcp2cli), and a runtime that consumes all of them (agentd). A shared mental model is cheaper than four private ones.

Canonical: tsok-org monorepo · docs/research/mcp.md