MCP Architecture: Host, Client, Server, and the JSON-RPC 2.0 Wire Format
MCP is a client-host-server protocol: one host (an AI application) drives any number of one-to-one client-server connections, all speaking JSON-RPC 2.0 over a transport layer that is kept separate from the message format itself. This split is what lets the same server serve unrelated AI applications without modification. See What Is the Model Context Protocol (MCP): the Open Standard Connecting AI Models to Tools and Data for the problem this solves and MCP Core Primitives: Tools, Resources, and Prompts — who is allowed to invoke what, and what a server must declare to offer it for what actually flows over these connections.
The client-host-server model
An MCP host — an AI application such as Claude Desktop or VS Code — establishes connections to one or more MCP servers, and can connect to multiple servers simultaneously (modelcontextprotocol.io/docs/learn/architecture). It does this by creating one MCP client per server connection: each client maintains a dedicated, one-to-one connection with its corresponding server, operating inside its own security boundary (modelcontextprotocol.io; codilime.com/blog/model-context-protocol-explained). A single host can therefore run several isolated client sessions at once — the example given is VS Code as host connecting to a Sentry server through one client and to a filesystem server through another, which prevents data leakage across domains (modelcontextprotocol.io). The three formal roles are: MCP Host (coordinates multiple clients), MCP Client (maintains the connection to one server), MCP Server (provides context to clients) (modelcontextprotocol.io).
Who enforces permissions
the host, not the client or server — it creates and controls the connections and enforces permissions and security policies for every server it's connected to (codilime.com/blog/model-context-protocol-explained).
Local vs. remote servers
local MCP servers using the STDIO transport typically serve a single client, while remote servers using Streamable HTTP typically serve many clients at once (modelcontextprotocol.io). See MCP Transports: stdio for Local Servers, Streamable HTTP for Remote Ones for the transport trade-offs this implies.
Data layer vs. transport layer separation
MCP is formally split into two layers: a data layer that defines the JSON-RPC-based protocol for client-server communication (message structure, lifecycle management, core primitives, notifications), and a transport layer that manages the communication channels and authentication — connection establishment, message framing, secure exchange (modelcontextprotocol.io). Because the data layer doesn't know or care which transport carries it, the same JSON-RPC 2.0 message format works unmodified across every transport mechanism (latenode.com/blog/model-context-protocol-architecture). MCP's protocol logic is the same regardless of which transport carries it — STDIO, HTTP+SSE, or emerging WebSocket protocols — but this is not runtime flexibility: the transport is a deployment-time decision, and the two established transports are not interchangeable once a server is running; a server is built and launched against one transport, not switched between them on the fly (latenode.com/blog/model-context-protocol-architecture).
Why split them
the layered design is credited with three benefits — modularity (components can be updated independently), security (access controls applied at each interface layer), and scalability (components scale independently) (graylog.org/post/what-is-the-model-context-protocol-mcp).
JSON-RPC 2.0 as the wire format
All communication between MCP clients and servers is carried as JSON-RPC 2.0 (modelcontextprotocol.io), and every message must conform to the JSON-RPC 2.0 spec (latenode.com/blog/model-context-protocol-json-rpc). JSON-RPC 2.0 itself is a stateless, lightweight, transport-agnostic messaging grammar using JSON — which is exactly why the same MCP server can work unchanged over stdio or over HTTP (latenode.com/blog/model-context-protocol-json-rpc).
MCP uses three JSON-RPC message types (medium.com/@vinodkrane, master-the-mcp-lifecycle):
- Requests — the client asks the server to perform an action, and expects a response carrying the matching id.
- Responses — the server returns a result or an error tagged with the corresponding request id.
- Notifications — one-way messages with no id and no expected reply, used for things like progress updates or capability signals (modelcontextprotocol.io).
Standard JSON-RPC fields are jsonrpc (protocol version), method (operation name), params, and id (medium.com/@vinodkrane). JSON-RPC only supplies the transport grammar; what a given tool actually expects — required fields, types, constraints — is separately described with JSON Schema 2020-12, which is what lets an LLM construct valid tool calls automatically (latenode.com/blog/model-context-protocol-json-rpc). See MCP Core Primitives: Tools, Resources, and Prompts — who is allowed to invoke what, and what a server must declare to offer it for how tool schemas are declared.
Capability negotiation at session start
MCP is stateful and requires explicit lifecycle management to negotiate which capabilities client and server each support (modelcontextprotocol.io). Initialization is mandatory and must happen first: no other request is allowed to proceed until it completes (medium.com/@vinodkrane).
The handshake sequence (modelcontextprotocol.io):
1. The client sends an initialize request declaring its protocol version and capabilities (tools, resources, prompts, sampling, logging, etc.).
2. The server responds with its own compatible protocol version and capabilities.
3. The client sends a notifications/initialized notification to signal it is fully ready for normal operation.
Enforcement
both parties must refrain from invoking any feature that wasn't agreed to during this handshake (codilime.com/blog/model-context-protocol-explained). Negotiation is granular — sub-capabilities like listChanged and subscribe let client and server agree on specific behaviors, not just broad feature categories (medium.com/@vinodkrane). A protocol version mismatch during negotiation triggers disconnection (medium.com/@vinodkrane). This granularity is also what gives the protocol forward-compatibility: a client can discover whether a given server supports experimental features, streaming, cancellation, or particular resource types before relying on them (latenode.com/blog/model-context-protocol-architecture). More detail on the full session lifecycle belongs in MCP Session Lifecycle: the Handshake a Client Must Get Right — and the One Being Removed in 2026-07-28.
How this architecture lets one server serve many AI applications
Tool definitions in MCP are stateless — name, description, and input schema aren't tied to any particular client — so a single server can serve multiple, unrelated AI applications without per-client customization (latenode.com/blog/model-context-protocol-architecture). Transport independence reinforces this: a remote server using HTTP+SSE can handle many concurrent clients through ordinary HTTP connection management while running identical protocol logic for all of them (latenode.com/blog/model-context-protocol-architecture), and the same protocol layer runs over STDIO, HTTP+SSE, or WebSocket without server functionality changing (latenode.com/blog/model-context-protocol-architecture).
Because capabilities are declared fresh at each session's handshake, servers can expose the same tools, resources, and prompts regardless of which application initiated the connection — servers stay agnostic to the identity of the host, enabling the same server to serve VS Code, Claude Desktop, ChatGPT, and others (modelcontextprotocol.io; latenode.com/blog/model-context-protocol-architecture).
See Enterprise MCP Integration Patterns — the Gateway Layer and the Build-vs-Buy Call Before You Ship a Server for gateway/proxy patterns and MCP Clients and Hosts: Claude, Cursor, VS Code, ChatGPT, Gemini — What 'Any Client' Actually Means for which hosts implement this client side today.
Verified against
- modelcontextprotocol.io/docs/learn/architecture
- codilime.com/blog/model-context-protocol-explained
- latenode.com/blog/model-context-protocol-json-rpc
- latenode.com/blog/model-context-protocol-architecture
- medium.com/@vinodkrane/master-the-mcp-lifecycle-step-by-step-gu…
- graylog.org/post/what-is-the-model-context-protocol-mcp
What links here
Source: Sinapsi — verified compositional memory, queryable by LLMs. Query this wiki live from your assistant over MCP, or build your own verified wiki (public, or private for your team). CC BY 4.0 — reuse with attribution to Sinapsi.