MCP architecture has three participants and two layers. An AI app (the host) creates one client for each server it connects to. Those clients and servers exchange JSON-RPC messages, which is the data layer, over stdio or Streamable HTTP, which is the transport layer. Since the 2026-07-28 revision, the protocol is also stateless. There's no handshake and no session, and every request carries its own protocol version and capabilities.

That's the short answer. This guide covers how the pieces fit when you actually deploy them. It includes a diagram of a remote setup, the path of one request through every component, what the stateless revision changed, and what it means for designing a server. Our MCP client vs server guide already covers what each role does, so this one focuses on the system as a whole. Everything below was checked against the 2026-07-28 specification on November 2, 2026.

MCP architecture at a glance

A 1920 cyanotype blueprint of a proposed marble bathtub, showing a plan view above and two cross-sections labelled Section A-B and Section C-D
"Förslag till badkar av Blanc Clair marmor," a 1920 proposal for a marble bathtub: one object, drawn as a plan and two sections. Unknown author, Hallwylska museet, via Wikimedia Commons, public domain.
PartWhat it isWhere it lives
HostThe AI app. Runs the model, keeps the conversation, asks the user to approve actionsClaude, ChatGPT, Cursor, VS Code, your own agent
ClientA connector object inside the host, one per serverInside the host. For web connectors, that often means the vendor's cloud
ServerA program that exposes tools, resources and promptsA subprocess on your machine (local) or an HTTP service (remote)
Data layerJSON-RPC 2.0 messages: discovery, primitives, notificationsIdentical on every transport
Transport layerHow messages are framed and delivered, plus authorizationstdio or Streamable HTTP
Authorization serverIssues OAuth tokens for a remote serverRun by the server operator or an identity provider
ExtensionsOptional features negotiated through capabilities, such as Tasks and MCP AppsOn top of the core protocol

The docs are clear about scope. MCP "focuses solely on the protocol for context exchange." It doesn't dictate how AI apps use models or manage the context they get. Which model you run, how you build prompts and what you store are your decisions. MCP defines the boundary between the app and the systems it reaches.

The MCP architecture diagram

The official diagrams show one host with several clients and a mix of local and remote servers. That's accurate, but it leaves out the parts you have to run in production. Here is the same architecture with a remote server deployed the way most hosted servers are.

MCP architecture diagram: a host app containing clients A, B and C. Client A launches a local server over stdin and stdout. Client C sends HTTPS POST requests with a bearer token and MCP headers through an optional gateway to a remote MCP server made of stateless instances. An auth server issues the token. Below the server sit a state store and upstream APIs, outside MCP.
MCP architecture under the 2026-07-28 spec. Client B would hold a third server. Diagram: DialMCP.

Reading it top to bottom:

  1. The host is the only part that sees everything. One of the spec's design principles is that servers "should not be able to read the whole conversation, nor 'see into' other servers." The host decides what each server receives.
  2. A local server is a child process. The client launches it and they talk over stdin and stdout. Credentials come from the environment. The spec says stdio implementations "SHOULD NOT" follow the OAuth authorization spec.
  3. A remote server is an HTTP endpoint. Every message is its own POST to one URL, usually ending in /mcp, carrying a bearer token and a few MCP headers.
  4. The authorization server sits beside the path, not on it. The client goes there once to get a token for this specific server, then presents it on each request.
  5. Gateways are optional. The 2026-07-28 headers exist so a load balancer or gateway can route without opening the body.
  6. The server can be many instances. Because requests are self-contained, any instance can answer any request.
  7. Below the server, MCP ends. Your database, your queue and the APIs you call are ordinary backend architecture.

Layer 1: the data layer

The docs describe MCP as two layers, and say "the data layer is the inner layer, while the transport layer is the outer layer." The data layer is the protocol itself, defined in JSON-RPC 2.0. Three things live here.

Message direction. Clients send requests and notifications. Servers send responses and notifications. In 2026-07-28 that's the complete list: the transport page says "No other message direction exists." A server never starts a request. When it needs something from the user mid-call, it ends the call early with an InputRequiredResult and the client retries with the answer. That pattern is called Multi Round-Trip Requests, and the client vs server guide walks through it.

Primitives. Servers offer tools (functions the model calls), resources (data the app loads by URI) and prompts (templates the user picks). Our tools, prompts and resources guide covers who controls each. On the client side, elicitation is the one active feature. Sampling and logging are deprecated as of 2026-07-28, and so are roots. The docs tell new implementations to call model APIs directly instead of sampling, and to log to stderr or OpenTelemetry.

Per-request metadata. Every request carries a _meta object with the protocol version and the client's capabilities. Clients should also include their name and version. Results carry a resultType: complete for a finished answer, input_required when the server needs more input first.

Layer 2: the transport layer

The spec calls a transport a "binding". It defines framing, delivery, how metadata travels and how cancellation works, but "does not define what the messages mean." That's why a tool behaves the same whether its server runs on your laptop or in a data center.

stdioStreamable HTTP
Who starts the serverThe client, as a subprocessThe operator, ahead of time
FramingOne JSON-RPC message per line on stdin/stdoutOne message per POST; reply is JSON or a request-scoped SSE stream
Clients per serverTypically oneTypically many
AuthEnvironment credentialsOAuth 2.1 bearer token, when the server requires auth
Cancelling a requestA notifications/cancelled messageClose that request's response stream
LoggingstderrYour own observability stack

Two details in the transport layer shape everything above it. First, authorization belongs here. The authorization spec opens by saying MCP provides "authorization capabilities at the transport level." The data layer never sees a token. Our MCP authorization guide covers the OAuth flow step by step.

Second, Streamable HTTP copies key body fields into headers. Every request POST must include MCP-Protocol-Version and Mcp-Method. tools/call, prompts/get and resources/read also carry Mcp-Name, set to the tool or prompt name or the resource URI. The spec says this is so "intermediaries (load balancers, gateways, observability tooling) can route and inspect requests without parsing the body." The body stays the source of truth, and a mismatch between header and body gets a 400. For the full history of the transport, including the deprecated HTTP+SSE version, see Streamable HTTP vs SSE.

One request, end to end

Diagrams hide the order of events, so here is a single tool call to a remote server, start to finish. Say a user asks their assistant for tomorrow's forecast and the host has a weather server connected.

  1. Discovery. At some point before this, the client called server/discover and tools/list, cached both, and the host merged the tool definitions into what the model can see.
  2. The model picks a tool. It emits a call to get_forecast with {"city": "Toronto"}. The host checks its approval rules and may ask the user first.
  3. The host routes it. The host finds the client that owns get_forecast and hands it the call.
  4. The client builds the request. It adds _meta, mirrors the headers, attaches the token and sends one POST.
  5. The edge checks it. A gateway can route on Mcp-Method and Mcp-Name, apply rate limits, and reject bad tokens before your code runs.
  6. An instance handles it. The server validates the token's audience, checks the version, runs the tool, calls whatever upstream API it needs with its own credentials, and returns a result.
  7. The result goes back to the model. The client passes the content to the host, which adds it to the conversation.

Step 4 on the wire looks like this:

POST /mcp HTTP/1.1
Host: weather.example.com
Authorization: Bearer eyJhbGciOi...
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_forecast

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "get_forecast",
    "arguments": { "city": "Toronto" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": { "elicitation": {} },
      "io.modelcontextprotocol/clientInfo": { "name": "example-host", "version": "3.1.0" }
    }
  }
}

Nothing in that request depends on an earlier one. Send it to any instance of the server and you get the same answer. That's what "stateless" means in this spec, and most of the architectural choices below follow from it.

If the server needs input partway through, for example a confirmation, step 6 returns an input_required result instead. The client gets the answer from the user and repeats steps 4 to 6 with a new request ID, the answer, and an opaque requestState the server gave it. If the user walks away, the client just never retries, and the spec says servers must not assume it will. Cancelling a request that is still running is a separate case. On Streamable HTTP, the client closes that request's response stream, and the spec says the server "MUST" treat that as cancellation.

Discovery replaced the handshake

Before 2026-07-28, every connection started with an initialize exchange that agreed a version and swapped capabilities, and over HTTP it usually created a session. That lifecycle is gone. Servers now "MUST implement" server/discover, which returns the versions they support, their capabilities and their identity. Clients "MAY" call it first, but they don't have to. A client can send any request straight away and, if the version is wrong, get an UnsupportedProtocolVersionError (code -32022) listing what the server accepts.

Discovery results are cacheable. Servers must attach caching hints to finished results from server/discover, tools/list, prompts/list, resources/list, resources/templates/list and resources/read. Those hints are ttlMs and cacheScope. The docs' example caches discovery for an hour and a tool list for five minutes, so a client can reuse those results instead of fetching them again. The TTL is a freshness hint, not a guarantee.

Change notifications became opt-in. A client that wants to hear when tools or resources change opens one long-lived subscriptions/listen stream and names the notification types it wants. It's the one long-lived stream the core protocol defines, and the docs call it best effort, so clients should still re-fetch when it matters.

What 2026-07-28 changed in the architecture

Most MCP architecture explainers still describe the 2025 design. The spec now calls revisions up to 2025-11-25 "legacy". The differences that matter at the architecture level:

Legacy (2025-11-25 and earlier)Modern (2026-07-28)
Starting a conversationinitialize handshakeNone; optional server/discover
State between requestsProtocol session, Mcp-Session-Id over HTTPNone in the protocol; servers mint their own handles
Server asks the client somethingServer-initiated requestInputRequiredResult, client retries
Server pushes changesHTTP GET stream, resources/subscribesubscriptions/listen stream, opt-in
Broken streamResumable with Last-Event-IDRequest is lost; the client may retry it (careful with tools that have side effects)
Scaling a remote serverSticky routing or shared session storageAny instance can serve any request

Old and new don't interoperate by default. A modern-only client fails against a legacy server, and the reverse fails too, because "Legacy clients have no fall-forward mechanism." Implementations that support both, which the spec calls dual-era, are how the ecosystem gets across. Expect to meet both kinds of server for a while.

MCP server architecture: designing a remote server

If you're building a server other people will connect to, the 2026-07-28 model gives you a fairly standard web-service shape. These are the decisions that matter. A few are spec requirements, marked as such; the rest is operational advice.

  • Run stateless instances behind an ordinary load balancer. No sticky sessions. That's the point of the stateless design. The Multi Round-Trip Requests page, for instance, says that pattern exists to avoid "requiring a shared storage layer across server instances or requiring stateful load balancing."
  • Put cross-call state behind handles. If a workflow spans calls, a tool returns an ID (a cart, a job, a call) and later calls take it as an argument. Store the state in your own database. The spec's security guidance says servers "MUST NOT treat possession of a state handle as authentication," and recommends random, unguessable handles bound to the authenticated user, so check ownership on every call.
  • Protect requestState. It comes back from the client, so the spec calls it attacker-controlled. If it affects authorization, resource access or business logic, the spec requires integrity protection, such as HMAC or AEAD. It also recommends binding it to the user, giving it a short lifetime, and enforcing single use on the server where that matters.
  • Design long work as start-then-check. A broken stream now loses the request, and the Tasks docs note that many clients and intermediaries "impose timeouts that make this impractical beyond a few seconds." Return a handle right away and let the client poll, or adopt the Tasks extension, which gives you a durable task ID with statuses such as working and completed.
  • Validate early. Checking the token's audience, the Origin header and header/body consistency are all spec requirements, and they're cheapest to do before your tool code runs. A gateway can do some of this; our MCP gateway explainer covers when that layer earns its keep.
  • Keep upstream credentials separate. The server is a resource server to MCP clients and often an API client to someone else. The spec's security best practices say token passthrough "is explicitly forbidden," so upstream calls use the server's own credentials.
  • Send cache hints you mean. If your tool list changes per user, scope the cache that way instead of marking it public.

None of this needs a special MCP platform. It's the same checklist as any authenticated JSON API, plus a few protocol-specific rules. Our MCP security checklist goes deeper on the risks, and remote vs local MCP servers covers when hosting is worth it at all.

Extensions: where the architecture grows

The core protocol is deliberately small. Optional features ship as extensions, advertised in the extensions field of the capabilities object under prefixed IDs. The spec's two examples are Tasks (io.modelcontextprotocol/tasks) for long-running work and MCP Apps (io.modelcontextprotocol/ui) for interactive UI returned by a server. The rule for mismatches is strict: if only one side supports an extension, it "MUST either revert to core protocol behavior or reject the request." So an extension can't silently break a client that has never heard of it, and server authors should plan a core-only fallback.

A real remote server, honestly mapped

We run DialMCP, a remote MCP server that lets an agent place phone calls from the user's own verified number. Here's how it maps onto the diagram, including the part that isn't modern yet.

  • Edge and auth server. It runs on Cloudflare Workers. An OAuth provider library sits in front, acting as the authorization server (registration, token and metadata endpoints) and gating /mcp behind bearer tokens. An unauthenticated request gets a 401 whose WWW-Authenticate header points to the protected-resource metadata, which is how clients find the sign-in flow. We re-checked that on November 2, 2026.
  • Server. The MCP endpoint is the Agents SDK's McpAgent on the 1.x TypeScript SDK. It still speaks the 2025 protocol with sessions, and each session lives in its own Durable Object. Durable Objects are addressed by name, so session routing is handled by the platform rather than a sticky load balancer.
  • State handles. Each phone call gets its own object, addressed by its call_id. place_call returns that ID immediately, and get_call takes it back, with an optional wait_seconds of up to 50 for a server-side long poll. Typical calls run 2 to 5 minutes, so start-then-check was the practical shape. It's close to what the Tasks extension formalizes.
  • Upstream. Behind the tools, the server dials through a telephony carrier's API (Telnyx) with its own credentials. The model never sees those.

So the call flow already fits the stateless model, and the session layer is the part a migration would replace. The full tool contract is on /docs/tools, and /docs/hosted covers what the endpoint speaks.

Further reading

Try a remote MCP server that does real work. DialMCP lets Claude, ChatGPT, Codex, Cursor or VS Code place real phone calls from your own verified number and hand back a transcript and a structured outcome. Add https://mcp.dialmcp.com/mcp as a connector and sign in. US and Canada, free during launch.

Connect DialMCP to your client

FAQ

What is MCP architecture?

MCP uses a client-host-server architecture. The host is the AI app, such as Claude, ChatGPT or Cursor. It creates one client for each server it connects to, and each server exposes tools, resources and prompts. Clients and servers exchange JSON-RPC messages (the data layer) over stdio or Streamable HTTP (the transport layer). Since the 2026-07-28 revision, every request is self-contained, with no handshake or session.

What are the two layers of MCP?

The data layer and the transport layer. The data layer is the JSON-RPC 2.0 protocol: discovery, tools, resources, prompts, elicitation and notifications. The transport layer defines how those messages are framed and delivered, over stdio for local servers or Streamable HTTP for remote ones, and it is where authorization lives. The same messages work on either transport.

What is the difference between an MCP host and an MCP client?

The host is the AI application the user works in. A client is a component the host creates to talk to one specific server, so a host connected to four servers runs four clients. The host runs the model, keeps the conversation and asks the user for approvals. Each client only handles the protocol for its own server.

Is MCP stateless?

Yes, as of the 2026-07-28 revision. The initialize handshake and protocol sessions were removed, and every request carries its protocol version and client capabilities in _meta. Servers that need state across calls return an explicit handle, such as a job ID, and accept it back as a tool argument. Revisions up to 2025-11-25 are stateful and are now called legacy.

How do you scale a remote MCP server?

Run several stateless instances behind an ordinary load balancer. Under 2026-07-28, any instance can answer any request, so you don't need sticky sessions. Keep cross-call state in your own store, keyed by the authenticated user and reached through handles. Return a handle and let the client poll for long-running work, or use the Tasks extension. Then send caching hints on tool and resource lists.