MCP architecture
The tool design chapter introduced the Model Context Protocol as a plug and socket: write a tool once as an MCP server and every AI application that speaks MCP can use it. This section goes inside the protocol. It matters because MCP has become the default way agents reach the outside world, from coding assistants opening your issue tracker to support bots querying an orders system, and because it changed substantially in 2026.
A note on dates, since this moves fast. Anthropic introduced MCP in November 2024. In December 2025 it was donated to the Agentic AI Foundation under the Linux Foundation, alongside other open agent standards. The specification is versioned by date, and this chapter follows the 2026-07-28 revision, the biggest change since launch: the protocol became stateless.
Earlier versions of MCP kept a session open between a client and a server. Why would a protocol designer remove sessions, and what does a server have to do instead if it needs to remember something between calls?
The three roles#
Host: the application the person uses. It talks to the model, decides which servers are connected, and asks the user for approval before risky calls.
Client: a connection from the host to one server.
Server: a program that exposes capabilities. It never talks to the model directly, and it does not know which model is on the other side.
Two transports carry the messages, which are JSON-RPC. stdio: the host starts the server as a local subprocess and they talk over standard input and output; used for tools on your machine. Streamable HTTP: the server is a web service; used for remote and shared servers.
What a server can offer#
Tools: functions the model can call, each with a name, a description and a JSON Schema for its inputs, and optionally a schema for structured output. This is most of what servers do.
Resources: read only data addressed by URI (orders://recent/17, file:///repo/README.md)
that the host can load into context.
Prompts: reusable templates a user can pick, such as "summarise this pull request".
Earlier versions also let servers reach back into the client: sampling (borrow the host's model for a completion) and roots (ask which folders it may touch). The 2026 revision deprecated both, along with protocol level logging. Pass folders as tool parameters, call model APIs directly, and log to stderr or OpenTelemetry instead.
Stateless: what changed in 2026#
Until 2026, a client opened a session: an initialize handshake, a session id header, and a
server that remembered things about that connection. Sessions made servers hard to scale, because
every request from a session had to reach the same server instance, which fights load balancers
and serverless platforms.
The 2026-07-28 specification removed all of that. Now:
- No handshake and no session id. Every request carries its own protocol version and the
client's capabilities in a
_metafield. Any server instance can answer any request. server/discoverlets a client ask, up front and optionally, which protocol versions and capabilities a server supports.- State that must persist becomes an explicit handle: the server returns an id (say, a cart or a browser session) and the model passes it back as an ordinary tool argument.
- List results are cacheable.
tools/listand friends carry a time to live, and servers should return tools in a deterministic order, which also helps model providers' prompt caching. - Notifications a client wants (tools changed, a resource updated) arrive on one opt-in
subscriptions/listenstream.
That answers the question at the top. Without sessions, a fleet of identical server instances behind a load balancer can serve any request, like any stateless web API. Anything that must be remembered is named explicitly, as a handle, instead of hiding in connection state.
Asking the user mid-call: multi round-trip requests#
Sometimes a tool needs more information halfway through: a confirmation, a choice, a login. The new spec handles this without server initiated requests.
Extensions#
Optional features now live in extensions outside the core protocol, which keeps the core small. Two official examples: Tasks, for long running operations that return a handle you poll later instead of blocking a request, and MCP Apps, for servers that provide interactive user interface components a host can render.
| Choice | What you gain | What you pay | Pick it when |
|---|---|---|---|
| stdio (local server) | No network, no auth setup, starts with the host. | Runs on the user's machine with their permissions, one copy per user. | Tools over local files, git, local databases, developer tools. |
| Streamable HTTP (remote server) | One deployment serves everyone, scales like a web API, and is centrally governed. | Needs authorization, and is a network service to secure and operate. | Company systems and SaaS integrations shared across users. |
| Explicit handles for state | Servers stay stateless and horizontally scalable. | The model must carry the handle between calls, and handles need expiry. | Multi step interactions: carts, browser sessions, long jobs. |
Checkpoint#
1. In the 2026-07-28 spec, how does a server learn the client's protocol version?
2. A server needs to remember a shopping cart across several tool calls. What is the stateless way?
3. How does a server ask the user a question in the middle of a tool call now?
- Host, client per server, server. Servers never see the model.
- Servers offer tools, resources and prompts. Sampling and roots are deprecated.
- 2026-07-28: stateless. No handshake or sessions, version in
_meta, state as handles. - input_required plus a retry replaces server initiated requests.
MCP connects AI applications to tools and data. The host is the app that owns the model and the user, it runs one client per server, and servers expose tools, resources and prompts over JSON-RPC, either locally over stdio or remotely over Streamable HTTP. Servers never talk to the model. The 2026-07-28 revision made the protocol stateless: there is no initialize handshake or session id, every request carries its protocol version and capabilities in _meta, servers optionally answer server/discover, and any state that must persist is an explicit handle passed as a tool argument, so servers scale like ordinary web APIs. When a tool needs user input mid-call it returns an input_required result and the client retries with the answers. Sampling, roots and protocol logging are deprecated, and optional features such as Tasks and MCP Apps live in extensions.