Module 9 18 min

MCP Architecture

Hosts, clients, servers, and why a protocol was needed.

You've built agents that call tools — and every tool meant writing custom glue code: one integration for weather, another for your database, another for files. Now multiply that by every AI app and every data source in the world. The Model Context Protocol (MCP) is the industry's answer: one open protocol so any AI application can talk to any tool server, the way USB lets any laptop talk to any device.

Why does this exist?

Before MCP, connecting M AI applications to N data sources meant M×N bespoke integrations — Claude needed its own GitHub plugin, so did your IDE, so did your chatbot. MCP collapses that to M+N: each app implements the protocol once as a client, each data source implements it once as a server, and everything interoperates. It's the same economic logic that gave us HTTP, SQL drivers, and the Language Server Protocol.

The problem: M×N integrations

Say three AI apps (a desktop assistant, an IDE, an internal chatbot) each need four capabilities (files, GitHub, Postgres, Slack). Without a standard, that's twelve integrations to write — each with its own auth handling, error formats, and update treadmill. Add one app or one data source and the count keeps multiplying. Worse, tool builders must choose which AI apps to support, and app builders must chase every API. A protocol breaks the multiplication: build a Slack MCP server once, and every MCP-capable app can use it, today and forever.

The architecture: hosts, clients, servers

MCP splits the world into three roles. Click around the diagram below — every box explains itself — then flip the transport toggle and hit Simulate request to watch a complete round trip with the actual JSON-RPC messages at each hop.

MCP architecture

Host

The application the user actually talks to — Claude Desktop, an IDE, or your own chat app. The host owns the conversation with the LLM and decides which MCP servers to connect to. It embeds one MCP client per server connection.

To recap the roles:

  • Host — the AI application the user actually uses (Claude Desktop, an IDE, your own app). It owns the model conversation and decides which servers to connect and what the user must approve.
  • Client — a protocol adapter living inside the host, one per server connection. It handles the handshake, capability discovery, and message routing. As an app developer you mostly get this from an SDK.
  • Server — a (usually small) program exposing capabilities: a filesystem server, a database server, a GitHub server. Servers are independent; they don't know about each other or about the model.

The wire format is JSON-RPC 2.0 — the same request/response-with-ids convention you've seen in the simulation. Over what wire? Two standard transports: stdio (the host launches the server as a local subprocess and they talk over stdin/stdout — perfect for local tools) and Streamable HTTP (the client POSTs to a remote server — perfect for hosted, shared services). Crucially, the messages are identical on both; transport is plumbing.

What servers expose: tools, resources, prompts

A server can offer three kinds of capability, distinguished by who controls their use:

  • Tools (model-controlled): actions the LLM decides to invoke — read_file, query_database, create_issue. Each is described with a name, description, and a JSON Schema for inputs. Hosts typically require user approval before execution.
  • Resources (application-controlled): data the host can read and attach to context — file contents, table schemas, documents — addressed by URI. The app or user picks them; the model doesn't fetch them on its own.
  • Prompts (user-controlled): reusable templates the server offers, surfaced in the host UI as commands the user invokes (think a /summarize-logs slash command shipped by the logging server).

The lifecycle of a request

Every session follows the same choreography:

  1. 1. InitializeClient and server shake hands: protocol version and capabilities ('I support tools and resources').
  2. 2. Discover
  3. 3. Model chooses a tool
  4. 4. tools/call
  5. 5. Result into context

Here's the pivotal message pair, verbatim:

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "list_directory",
    "arguments": { "path": "~/project" }
  }
}
{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "content": [
      { "type": "text", "text": "README.md\nsrc/\npackage.json" }
    ]
  }
}

Nothing exotic — a method name, arguments matching the advertised schema, and a matching-id reply. The power is that every MCP server in the world speaks exactly this.

Servers run code on someone's machine

An MCP server is a program with real permissions — a filesystem server can read files, a database server can run queries. Only install servers you trust, scope their access narrowly (allowed directories, read-only DB users), and keep human approval on for destructive tools. The protocol standardizes communication, not safety.

Build it yourself

  1. If you use Claude Desktop or an MCP-capable IDE, install the official filesystem server and connect it to a scratch directory. Watch the approval prompt appear when the model first calls a tool.
  2. Ask the assistant to list and summarize the files. Then ask yourself: which hops from the simulation just happened invisibly?
  3. Sketch (on paper) the M×N diagram for your own stack: which AI surfaces do you have, and which internal systems would you wrap as servers? That sketch is next lesson's motivation — where you'll build a server from scratch.

Summary

  • MCP is an open protocol turning M×N AI-to-tool integrations into M+N: apps implement a client once, data sources implement a server once.
  • Three roles: host (user-facing app), client (protocol adapter inside the host, 1:1 per server), server (exposes capabilities).
  • Servers expose tools (model-controlled), resources (app-controlled), and prompts (user-controlled).
  • Wire format is JSON-RPC 2.0 over stdio (local subprocess) or Streamable HTTP (remote) — identical messages either way.
  • Sessions follow initialize → discover (tools/list) → call (tools/call) → result into model context.