# MCP Architecture: How MCP Works (2026)

> How MCP works: the client-server architecture behind the Model Context Protocol — host, client, server — and how data flows through a tool call.

Source: https://designrevision.com/blog/how-mcp-works

---

**MCP works on a client-server architecture with three roles — a host (the AI app), a client inside it, and servers that wrap external tools — exchanging JSON-RPC messages so a model can discover and call real capabilities.** When you ask an AI to do something it can't answer from memory, that request flows through this architecture — the open standard [Anthropic introduced in 2024](https://www.anthropic.com/news/model-context-protocol) — and back with live results. This guide walks the full MCP architecture and the exact data flow, one step at a time. For the concept in plain terms first, see [what MCP is](/blog/what-is-mcp); this page is the deeper "how it's built and how data moves" companion.

*Last updated: July 2026. The MCP architecture below is stable, but transports and extensions evolve — see the [official architecture docs](https://modelcontextprotocol.io/docs/learn/architecture) for the current spec.*

## The MCP Architecture: Three Roles

MCP is a **client-server** protocol, but the accurate picture has *three* parts, not two:

- **Host** — the user-facing AI application: Claude Desktop, Cursor, VS Code, or a custom agent. It's what you interact with, and it holds one or more clients.
- **Client** — a connector *inside* the host that manages exactly one connection to one server. It handles the handshake, session, and capability discovery for that server.
- **Server** — a lightweight, standalone program that wraps an external system (GitHub, a Postgres database, your filesystem) and exposes its capabilities in a standard way.

The relationship is one host → many clients → one server each. So a single editor can hold a client for a GitHub server and another for a database server simultaneously, each isolated.

```text
┌─────────────────────────── HOST (Claude, Cursor, VS Code) ───────────────────────────┐
│                                                                                       │
│   ┌─────────┐        JSON-RPC         ┌──────────────┐   wraps    ┌────────────────┐  │
│   │ Client  │ ─────────────────────▶  │  MCP Server  │ ─────────▶ │  GitHub API    │  │
│   └─────────┘   stdio or HTTP/SSE      └──────────────┘            └────────────────┘  │
│   ┌─────────┐                          ┌──────────────┐   wraps    ┌────────────────┐  │
│   │ Client  │ ─────────────────────▶  │  MCP Server  │ ─────────▶ │  Postgres DB   │  │
│   └─────────┘                          └──────────────┘            └────────────────┘  │
│        ▲  the model decides which tool to call                                         │
└────────┼──────────────────────────────────────────────────────────────────────────────┘
         │
      you ask the AI to do something
```

*The MCP architecture diagram above: one host holds multiple clients, each connected to a server that wraps a different real system.*

## How Data Flows Through MCP

Here's the sequence when you ask an AI something that needs a real tool — the heart of how MCP works:

1. **Capability discovery.** On connecting, the client asks the server what it offers, and the server returns its list of **tools**, **resources**, and **prompts** with their schemas. The model now knows what's available.
2. **Context enrichment.** Relevant resources the server exposes (a file, a record) can be pulled into the model's context before it answers, grounding it in real data.
3. **Tool invocation.** When an action is needed, the model emits a **structured tool call** — which tool, with which arguments. The client relays that call to the server.
4. **Execution and response.** The server translates the call into the underlying system's real operation (an API request, a SQL query), runs it, and returns a structured result. The model reads the result and produces its answer — or takes the next step.

That workflow — discover, enrich, invoke, execute — is the whole engine, and it runs the same way every time. Everything else is detail in service of it.

## Under the Hood: JSON-RPC and the Handshake

The messages themselves are **[JSON-RPC 2.0](https://www.jsonrpc.org/specification)** — the same request/response/notification format whichever transport is used. When a client first connects, it runs an **initialize** handshake with the server: the two exchange protocol versions and **negotiate capabilities** (what each side supports), and only then does normal traffic begin. From there, three message kinds flow: **requests** (expecting a reply, like a tool call), **responses** (the reply), and **notifications** (one-way, like a list-changed event). It's deliberately unexotic — a known, debuggable protocol rather than a bespoke one.

A tool call on the wire is just a plain JSON-RPC request and its response:

```json
// Request — the client asks the server to run a tool
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "get_component", "arguments": { "name": "dialog" } } }

// Response — the server returns the result
{ "jsonrpc": "2.0", "id": 1,
  "result": { "content": [ { "type": "text", "text": "// dialog.tsx source…" } ] } }
```

The connection is **stateful**: the session stays open after the handshake, so discovery runs once and every later tool call reuses it. And when a call fails, the server returns a JSON-RPC **error** object — a `code` and `message` — instead of a `result`, which the host surfaces so the model can retry or report it rather than silently guessing.

## Transports: stdio vs. Streamable HTTP

The architecture supports two transports, and the choice is just *where the server runs*:

- **stdio** — the host launches the server as a **local subprocess** and talks to it over standard input/output. Used for local servers (a filesystem or git server on your machine).
- **Streamable HTTP (with SSE)** — the client connects to a **remote server** at a URL, with Server-Sent Events for streaming. Used for hosted servers you reach over the network.

| | stdio | Streamable HTTP (SSE) |
|---|-------|------------------------|
| **Server runs** | Locally, as a subprocess | Remotely, behind a URL |
| **Latency** | Lowest — no network hop | A network round-trip |
| **Auth** | Inherited from your shell | Headers / OAuth |
| **Best for** | Local tools (files, git) | Hosted, shared, multi-user servers |

The JSON-RPC messages are identical across both; only the pipe changes. This is why the [same server can be added to different clients](/blog/mcp-clients-compared) with only a config tweak.

## What the Server Actually Exposes

A server's capabilities come in the three MCP **primitives**, each with a different owner:

| Primitive | Controlled by | Role in the flow |
|-----------|---------------|------------------|
| **Tools** | The model | Actions the model chooses to invoke (step 3 above) |
| **Resources** | The app | Data pulled into context (step 2 above) |
| **Prompts** | The user | Reusable templates the user triggers |

Most of the traffic is **tools** — the actions. We go deeper on the primitives and the protocol's origins in [what MCP is](/blog/what-is-mcp).

## A Worked Example

Say you ask Cursor, "add a confirmation dialog to the settings page." Cursor (the **host**) has a **client** connected to a [DesignRevision MCP](/mcp) **server**. The flow: the client already **discovered** a `get_component` tool at connect time; the model **invokes** it with the argument `dialog`; the client relays the JSON-RPC call; the server **executes** it against the [component registry](/components) and returns the real shadcn/ui dialog source plus its install command; Cursor applies it. Four steps, one dialog in your project — no custom integration written for Cursor at all. That's the architecture doing its job.

## Conclusion

How MCP works comes down to a simple architecture doing a tight loop: a **host** holds **clients**, each connected to a **server** that wraps a real system, and they exchange **JSON-RPC** messages to **discover, enrich, invoke, and execute**. Transports (stdio or HTTP/SSE) decide where the server runs; the primitives (tools, resources, prompts) decide what it offers; the four-step data flow ties it together. Understand that loop and every MCP setup — local or remote, one server or ten — is the same shape. Next: [what MCP is](/blog/what-is-mcp) for the concept, or [how to add a server](/blog/add-mcp-server-to-claude-code) to put it to work.

---

## Related Resources

- [What Is MCP? A Frontend Developer's Guide](/blog/what-is-mcp)
- [MCP vs API: What's the Difference? (2026)](/blog/mcp-vs-api)
- [MCP Clients Compared: Every AI Coding Agent](/blog/mcp-clients-compared)
- [MCP Tools Reference — Parameters & Examples](/mcp/tools)
- [DesignRevision MCP — the shadcn/ui MCP server](/mcp)
- [How to Add an MCP Server to Claude Code (2026 Guide)](/blog/add-mcp-server-to-claude-code)
