mcpstepbystep

Tutorials Foundations

What is MCP? How the Model Context Protocol actually works

The Model Context Protocol explained: the N×M problem, hosts, clients and servers, tools, resources and prompts, transports, and how a session starts.

updated 2026-07-19 ⏱ 15–20 min

MCP — the Model Context Protocol — is an open standard that lets AI applications talk to external tools and data through one shared interface. Anthropic released it in November 2024; today it is the default way Claude, ChatGPT, Cursor, VS Code, and most agent frameworks connect to the outside world.

Before you write a line of server code, it pays to understand what the protocol actually is. This page gives you the model everything else on this site builds on.

The problem MCP solves: N×M integrations

Say you have 4 AI applications (Claude Desktop, Cursor, a support bot, an internal agent) and 6 things they need to reach (GitHub, Postgres, Slack, a file system, a ticketing system, a search API).

Without a standard, every pairing is a custom integration: 4 × 6 = 24 connectors, each with its own auth handling, its own tool-schema format, its own bugs. Add a fifth app and you owe six more.

MCP turns N×M into N+M. Each application implements the protocol once as a client. Each tool or data source is wrapped once as a server. Any compliant client can then use any compliant server. It is the same trick USB-C pulled on device chargers, and the same trick the Language Server Protocol pulled on editor–language integrations — LSP is the explicit design inspiration.

Hosts, clients, and servers

MCP defines three roles, and the naming trips people up, so let’s be precise:

  • Host — the AI application the user actually sees: Claude Desktop, Cursor, VS Code, your own agent. The host owns the LLM, the UI, and all permission decisions.
  • Client — a protocol connector inside the host. The host creates one client per server it connects to, and the client maintains that one-to-one session.
  • Server — the program exposing capabilities: tools, resources, prompts. It can be a 50-line script on your laptop or a hosted service behind OAuth.
graph LR
  subgraph Host["Host app (Claude Desktop, Cursor, VS Code)"]
    LLM[LLM]
    C1[MCP client 1]
    C2[MCP client 2]
  end
  C1 -->|stdio| S1["Local server (files, git)"]
  C2 -->|Streamable HTTP| S2["Remote server (SaaS API)"]

The part worth memorising: servers never talk to the model. A server answers protocol requests. The host decides what the model sees, and the user (through the host) approves what runs. That separation is why one server can safely serve many different hosts.

What a server offers: tools, resources, prompts

A server exposes up to three kinds of things, and choosing the right one is most of MCP design:

PrimitiveControlled byWhat it isExample
ToolsThe modelFunctions the LLM may call (with user approval)search_notes, create_issue
ResourcesThe applicationRead-only data the host can attach as contexta file, a database schema, notes://recent
PromptsThe userReusable prompt templates the user invokes explicitlya /weekly-review slash command

Tools get all the attention because they are what makes agents act. But resources and prompts exist for a reason: not everything should be a tool. If the data just needs to be readable, a resource costs no tool-call round trip; if the user should drive, a prompt keeps them in control.

A practical warning while we are here: every tool you expose is injected into the model’s context as a definition. Large servers get expensive — a 106-tool server has been measured consuming roughly 54,000 tokens before the conversation even starts. Fewer, better-described tools beat exhaustive API mirrors. We return to this in the tool-design material further along the path.

What a client offers back

The protocol is two-way. A client can declare capabilities the server may use:

  • Elicitation — the server asks the user for input mid-operation (“Which repository?”), through the host’s UI. Since spec 2025-11-25 this includes URL mode, where the server hands the user a link to complete a step in the browser.
  • Sampling — the server asks the host’s LLM to complete something, without holding its own API key.
  • Roots — the host tells the server which directories it is allowed to care about.

Support for these varies widely across hosts, and the 2026-07-28 spec revision deprecates roots, sampling, and server-initiated logging (with a 12-month window) in favour of a leaner core. Build nothing critical on them; know they exist so the capability lists make sense.

What actually goes over the wire: JSON-RPC and the lifecycle

Everything in MCP is JSON-RPC 2.0 — small JSON messages with a method, params, and an id. A session (as of spec 2025-11-25) has three phases:

  1. Initialize. The client sends initialize with the protocol version it speaks and its capabilities. The server replies with its own version, capabilities, and name. The client confirms with a notifications/initialized notification. This handshake is how both sides learn what the other can do — a client that doesn’t support elicitation will never be asked for it.
  2. Operate. The client calls things like tools/list, tools/call, resources/read, prompts/get. Either side can send notifications — for example notifications/tools/list_changed when the server’s tool set changes.
  3. Shut down. No special message; the transport closes.

You will meet this lifecycle concretely the first time a server misbehaves: MCP error -32000: Connection closed is JSON-RPC vocabulary for “the server process died before or during the handshake”.

Transports: stdio and Streamable HTTP

The JSON-RPC messages need a pipe to travel through. There are two that matter:

  • stdio — the host launches the server as a subprocess and speaks over stdin/stdout. Zero network setup, credentials stay local. This is the default for anything running on your own machine, and it is why a stray print() to stdout can corrupt the protocol stream.
  • Streamable HTTP — a single HTTP endpoint (conventionally /mcp) that can stream responses. This is the transport for remote and shared servers, and it carries the auth story (OAuth 2.1).

You will also see SSE (HTTP+SSE) in older tutorials and client menus. It was the original remote transport, deprecated since spec 2025-03-26 in favour of Streamable HTTP. Clients keep legacy support, but build new servers on Streamable HTTP only.

Rule of thumb: local and personal → stdio; shared, hosted, or multi-user → Streamable HTTP.

Who owns the spec, and how versions work

MCP versions are dates, and claims about MCP should be pinned to them. The line so far: 2024-11-05 (initial) → 2025-03-26 (Streamable HTTP, OAuth 2.1, tool annotations) → 2025-06-18 (elicitation, structured tool output, the server reclassified as an OAuth resource server) → 2025-11-25, the current version (experimental Tasks, URL-mode elicitation, OIDC discovery, JSON Schema 2020-12 as the default schema dialect).

On governance: since December 2025, MCP is no longer a single-vendor project. Anthropic donated it to the Agentic AI Foundation (AAIF) under the Linux Foundation, co-founded with Block and OpenAI, with Google, Microsoft, AWS, and Cloudflare among the members. For anyone betting a roadmap on MCP, that is the strongest signal available that the protocol outlives any one vendor’s strategy.

What changes on 2026-07-28

The next revision finalises on July 28, 2026, and it is a significant simplification: a stateless core. The initialize handshake and session IDs go away, making servers easier to scale and load-balance; an extensions framework moves optional features out of the core; Tasks are redesigned for long-running work; MCP Apps add interactive UI; and roots, sampling, and logging are deprecated with a 12-month window. Everything on this site is written against 2025-11-25 and flags the places 2026-07-28 will touch — the mental model on this page survives the change intact.

Troubleshooting

spawn npx ENOENT (or spawn uv ENOENT) in the client logs A stdio-transport failure, not a protocol one: the host tried to launch your server as a subprocess and couldn’t find the executable, because GUI apps don’t inherit your shell’s PATH. Use an absolute path to the command in the config. On Windows, npx may also need to run via cmd /c npx.

MCP error -32000: Connection closed JSON-RPC error vocabulary for “the server process exited before or during the initialize handshake”. The server crashed on startup — run it manually in a terminal to see the real stack trace.

The server connects, but its tools never appear Usually a lifecycle issue: the client caches the tools/list result from initialization. Fully restart the host application (on Claude Desktop, quit from the menu bar/tray — closing the window is not enough), and confirm the server actually registers tools before it starts serving.

An older guide tells you to use an “SSE endpoint” and your client refuses You are following pre-2025-03-26 material. HTTP+SSE was replaced by Streamable HTTP; some clients now reject or warn on SSE-only servers. Prefer Streamable HTTP for anything new, and treat SSE options in client menus as legacy compatibility.

Next step

You can now draw MCP on a whiteboard; time to make it real. In the next tutorial you’ll build a working MCP server in Python — two tools, a resource, and a prompt — and connect it to Claude in under an hour.

newsletter

One practical MCP guide in your inbox. No news, no hype.

Tutorials and decision frameworks as they ship. Unsubscribe anytime.