MCP Client vs Server (and Host): Roles Are Per-Connection, Not Per-App
Almost every explanation of MCP client vs server teaches you the wrong thing: it defines the roles by the handshake. The client is the one that sends initialize, the server is the one that answers. That is a mechanism, not a role — and on 28 July 2026, eleven days from now, that mechanism disappears from the specification. The roles do not. This is the version of the distinction that still works on 29 July.
The 30-second answer
The Model Context Protocol specification overview defines the three roles in one breath: Hosts are “LLM applications that initiate connections”, Clients are “Connectors within the host application”, and Servers are “Services that provide context and capabilities”. That is the whole distinction. Everything else is implementation.
The reason the question keeps getting asked anyway is that almost every answer on the internet defines the roles by the handshake — the client is whoever sends initialize. That definition has an expiry date. A release candidate for spec revision 2026-07-28 was locked on 21 May 2026, and per the MCP blog, the final specification will be published on July 28, 2026. It removes the handshake outright: “The initialize/initialized handshake is removed (SEP-2575)” and “The Mcp-Session-Id header and the protocol-level session that came with it are also removed (SEP-2567)”. The draft says it flatly: “MCP is a stateless protocol” and “There is no negotiation handshake.”
And yet the draft architecture page still reads: a “client-host-server architecture where each host can run multiple client instances”, with “each client having a 1:1 relationship with a particular server.” The roles are the stable layer. The handshake was never the definition — it was a symptom. So learn the roles the way the protocol actually derives them.
One dating note, because half the confusion in this topic is people quoting different eras at each other: as of today, 17 July 2026, the current published revision is 2025-11-25. It stays the shipping reality for every legacy server, indefinitely. Everything below is date-stamped.
Three questions, three roles
Do not memorise three kinds of software. Derive three jobs from three ownership questions. Every role in MCP falls out of exactly one of them.
1. Who owns the model and the conversation? The host. Per the docs, the “MCP Host” is “The AI application that coordinates and manages one or multiple MCP clients”. It is the only party that sees everything. The spec’s design principles are unusually candid about why: “Servers should not be able to read the whole conversation, nor ‘see into’ other servers” — “Full conversation history stays with the host”, “Each server maintains isolation”, “Host process enforces security boundaries”. Consent is host territory too: hosts “must obtain explicit user consent before exposing user data to servers” and “before invoking any tool”. The spec then concedes the uncomfortable part — “MCP itself cannot enforce these security principles at the protocol level”. That is a host’s job description, not a protocol feature.
2. Who owns exactly one connection? The client. Not “the app”. A client is a per-server connection object: “A component that maintains a connection to an MCP server and obtains context from an MCP server for the MCP host to use”. The docs are unambiguous about the mechanics: “The MCP host accomplishes this by creating one MCP client for each MCP server. Each MCP client maintains a dedicated connection with its corresponding MCP server.” VS Code with three servers configured is one host holding three clients. This is why “MCP client for Claude” is a malformed query — Claude Desktop is a host; the client is the invisible thing it spawns per server.
3. Who owns a capability and no model? The server: “A program that provides context to MCP clients”. No conversation, no model, and — in the modern era — never the initiator of a round trip.
The spec offers its own lineage for this shape, and it beats the tired USB-C line: “MCP takes some inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across a whole ecosystem of development tools.” Editor is to language server as host is to MCP server. The client is the per-language-server connection your editor manages and you never think about. That mapping is the spec’s, not a blogger’s. If you want the adjacent framing — protocol versus plain REST — we cover that in MCP vs API.
The 1:1 rule everyone inverts
Here is the single most common error in MCP explainers, and it is worth being pedantic about because it inverts the spec. People read “A host application creates and manages multiple clients, with each client having a 1:1 relationship with a particular server” and conclude that each server has exactly one client. It does not.
The 1:1 binds the connection. “Each client is created by the host and communicates with exactly one server” — that is a constraint on the client. Look at the architecture overview in the MCP docs and you will find Client 3 and Client 4 both connected to remote Server C. (The specification’s own architecture diagram is no help here: it draws three clients on three separate servers, which is part of why the misreading spreads.) The docs confirm the asymmetry directly: “Local MCP servers that use the STDIO transport typically serve a single MCP client, whereas remote MCP servers that use the Streamable HTTP transport will typically serve many MCP clients.” One client → one connection → one server. One server → potentially thousands of clients.
Proof: one binary, both roles
If roles were properties of applications, the following configuration would be a paradox. It is not; it is a documented feature.
Claude Code is a host — it reads your config and mints a client per server. Claude Code is also a server: claude mcp serve starts “Claude as a stdio MCP server”, and the documentation shows you wiring that into Claude Desktop’s claude_desktop_config.json:
{
"mcpServers": {
"claude-code": {
"type": "stdio",
"command": "claude",
"args": ["mcp", "serve"],
"env": {}
}
}
}
Same binary. Both roles. At the same time. Claude Desktop is now the host; a client it spawned holds one stdio connection; on the other end of that connection Claude Code is a server — while Claude Code, in its own process, is still a host to its own configured servers. The docs even spell out the consequence for consent: “This MCP server only exposes Claude Code’s tools to your MCP client, so your own client is responsible for implementing user confirmation for individual tool calls.” Responsibility follows the role you hold on that connection, not the name on the executable.
This is why the question “is X a client or a server?” is usually unanswerable as asked. The honest answer is: on which connection?
Building a host? You still need a model behind it.
MCP standardises the capability side. It says nothing about where your completions come from. DataLLM Lab is an OpenAI-compatible gateway at https://www.datallmlab.com/v1 — 300+ models on one key, so your host can route tool-calling to one model and summarisation to a cheaper one without a second integration.
One caveat we are obliged to state against our own interest, because it is directly relevant if you use a gateway. Claude Code’s tool search “keeps MCP context usage low by deferring tool definitions until Claude needs them. Only tool names and server instructions load at session start,” and it “is enabled by default” — so the recycled claim that every MCP tool permanently eats your context is false under defaults. But: it “is also disabled when ANTHROPIC_BASE_URL points to a non-first-party host, since most proxies don’t forward tool_reference blocks. Set ENABLE_TOOL_SEARCH explicitly to override either fallback.” It is likewise off by default on Google Cloud’s Agent Platform, and it needs a model that supports tool_reference blocks. Point Claude Code at any gateway — ours included — and you pay full tool-definition cost unless you flip that flag. Budget for it, or read context engineering for how to pay less.
Who owns what
Run every MCP responsibility through the ownership questions and the table writes itself. The highlighted cells are the ones people most often assign to the wrong role.
| Responsibility | Host | Client | Server |
|---|---|---|---|
| Owns the model and the conversation | Yes — full history stays here | No | No — cannot see into the chat |
| Owns exactly one connection | No — holds one client per server | Yes — 1:1 with its server | No — may face many clients |
| Exposes tools, resources, prompts | No | No | Yes — the three server primitives |
| Obtains explicit user consent | Yes — spec assigns this to the host | No | No |
| Enforces isolation between servers | Yes — host process enforces boundaries | No | No |
| Launches the stdio subprocess | Decides config, scope and trust | Yes — the client launches it | No — it is the subprocess |
| OAuth 2.1 role (HTTP transports only) | Not a party | OAuth client | Protected resource server |
| Answers sampling requests with a model | Yes — via its client | Relays and gates the request | Asks; owns no model |
| Code you will actually write | Occasionally | Rarely — the SDK mints it | Almost always |
The primitive split is the tell. Servers get three: Tools (“Executable functions that AI applications can invoke”), Resources (“Data sources that provide contextual information”), Prompts (“Reusable templates that help structure interactions”), reached through a */list, */get, tools/call pattern. Clients get their own — the half most articles omit entirely. Per the spec overview: Sampling, Roots and Elicitation. (Fair warning: the docs contradict themselves here. The architecture overview lists Sampling/Elicitation/Logging and drops Roots; client-concepts lists Elicitation/Roots/Sampling. Take the spec’s list; Logging is really a server→client utility.)
Sampling is the proof that a client is not a dumb pipe, because it inverts the arrow: “Sampling allows servers to request LLM completions through the client” — “servers can request that the client—which already has AI model access—handle these tasks on their behalf. This approach puts the client in complete control of user permissions and security measures.” The server gets model access without shipping a model SDK or paying for inference. Roots are the other great myth-buster: they tell a server which file:// directories are in scope, and they are advisory. “While roots communicate intended boundaries, they do not enforce security restrictions. Actual security must be enforced at the operating system level, via file permissions and/or sandboxing.” The docs explain why the spec says servers “SHOULD respect root boundaries” rather than MUST enforce them: “servers run code the client cannot control.”
Do note the deprecation, because it lands with the same revision: under the new feature lifecycle policy (SEP-2596), Roots, Sampling and Logging are deprecated (SEP-2577) as of 2026-07-28, with “at least twelve months between deprecation and the earliest possible removal.” Deprecated is not removed — they remain in the spec and they still work. But do not architect a 2026 product on Sampling as its foundation.
Who initiates what
“The client always initiates” is false today and true in eleven days. This is the section where date-stamping earns its keep, and the draft hands us the vocabulary to do it: Legacy = “protocol versions that establish a session with an initialize handshake (2025-11-25 and earlier)”; Modern = “protocol versions that convey version, identity, and capabilities as per-request metadata (revision 2026-07-28 and later)”; Dual-era = “an implementation that supports both modern and legacy versions.” The draft ships a compatibility matrix, and the two headline cells are unforgiving: Modern client + Legacy server = fails; Legacy client + Modern server = fails.
Legacy (2025-11-25, current today). Three phases: Initialization, Operation, Shutdown. “The initialization phase MUST be the first interaction between client and server.” “The client MUST initiate this phase by sending an initialize request”, and “After successful initialization, the client MUST send an initialized notification” (notifications/initialized). Before the initialize response the client SHOULD NOT send anything but pings; before the initialized notification the server SHOULD NOT send anything but pings and logging. Version negotiation rides on that request: the client MUST send a version it supports, and “If the server supports the requested protocol version, it MUST respond with the same version. Otherwise, the server MUST respond with another protocol version it supports” — and if the client cannot live with the answer, it SHOULD disconnect. Shutdown, notably, has no protocol message at all: it is transport-level. Over stdio the client SHOULD close the input stream, wait for exit, then SIGTERM, then SIGKILL. Over HTTP you just close the connection. Half the “MCP lifecycle” diagrams online invent a shutdown message that does not exist.
And in this era, servers can initiate. They send their own JSON-RPC requests to clients over SSE streams — that is how sampling and elicitation worked from 2025-03-26 through 2025-11-25.
Modern (2026-07-28, published 28 July 2026). No handshake, no session. “Every request carries its protocol version, and the server accepts or rejects each request independently.” Discovery moves to a method: “Servers MUST implement server/discover. Clients MAY call it before sending any other requests to learn the server’s supported versions up front, but are not required to.” Client capabilities now ride along on every request in _meta.io.modelcontextprotocol/clientCapabilities. And the arrow inverts back: per MRTR (SEP-2322), “Server-to-client interactions (sampling, elicitation, list-roots) are embedded as input requests inside an InputRequiredResult per MRTR, not delivered as separate requests on this or any other stream. This is a change from Streamable HTTP in protocol versions 2025-03-26 through 2025-11-25, where servers could send such requests on SSE streams.” In the modern era, the client initiates every round trip. Full stop.
Notice what did not change across that rewrite. Who owns the model. Who owns one connection. Who owns a capability. If you learned client-vs-server as “the client sends initialize”, your mental model expires on 28 July 2026. If you learned it as ownership-of-a-connection, you will not notice.
Transports, auth, and non-roles
A great deal of client/server confusion is really transport confusion wearing a disguise. The spec is narrow: as of 2025-11-25 there are exactly two standard mechanisms, stdio and Streamable HTTP, and “Clients SHOULD support stdio whenever possible” — though clients and servers “MAY implement additional custom transport mechanisms.” A third name floats around: HTTP+SSE from 2024-11-05, which Streamable HTTP “replaces”, and which the draft classifies formally as Deprecated with “New implementations SHOULD NOT adopt it.” Three names in the wild, one of them dead: that alone explains a lot of bad blog posts.
stdio is where the roles get physical. “The client launches the MCP server as a subprocess.” “The server MUST NOT write anything to its stdout that is not a valid MCP message” — the classic way to break your own server is a stray print(). Logging goes elsewhere: the server “MAY write UTF-8 strings to its standard error (stderr) for any logging purposes”, and “The client MAY capture, forward, or ignore the server’s stderr output and SHOULD NOT assume stderr output indicates error conditions.” Messages are newline-delimited and MUST NOT contain embedded newlines.
Streamable HTTP (2025-11-25) puts the server behind “a single HTTP endpoint path (the MCP endpoint) that supports both POST and GET”; clients POST each message with an Accept header listing both application/json and text/event-stream, and MUST send MCP-Protocol-Version on subsequent requests. Servers MUST validate the Origin header and MUST return 403 if invalid (DNS-rebinding defence), and local servers SHOULD bind to 127.0.0.1 only. That transport changes shape in 2026-07-28 too: the GET stream endpoint, protocol-level sessions and Last-Event-ID resumability all go, replaced in part by a subscriptions/listen request, and two new headers mirror body fields so load balancers can route without parsing bodies — Mcp-Method on all requests and Mcp-Name on tools/call, resources/read and prompts/get. “These headers are REQUIRED for compliance”, and a header/body mismatch MUST be rejected with 400 and JSON-RPC error -32020 (HeaderMismatch). If you run anything remote, read the remote MCP server guide before the 28th.
Two clarifications that stop arguments. Local vs remote is not a kind of server, it is a transport choice: “Note that MCP server refers to the program that serves context data, regardless of where it runs.” And MCP does not simply “use OAuth”. “Authorization is OPTIONAL for MCP implementations.” “Implementations using an HTTP-based transport SHOULD conform to this specification. Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment.” Where it does apply, the OAuth roles map onto the MCP roles cleanly: “A protected MCP server acts as an OAuth 2.1 resource server”; “An MCP client acts as an OAuth 2.1 client, making protected resource requests on behalf of a resource owner”; the authorization server is a third party. It is OAuth 2.1 (IETF draft draft-ietf-oauth-v2-1-13), not 2.0. Servers MUST implement Protected Resource Metadata (RFC 9728), clients MUST use PKCE with S256 and MUST refuse to proceed if the AS does not advertise code_challenge_methods_supported, clients MUST send the RFC 8707 resource parameter, and servers MUST validate token audience and “MUST NOT pass through the token it received from the MCP client”. One update that invalidates a lot of 2025-era tutorials: OAuth Client ID Metadata Documents (SEP-991) are now the recommended registration mechanism — clients and authorization servers “SHOULD support” them — and Dynamic Client Registration (RFC 7591) is demoted to being “included for backwards compatibility with earlier versions of the MCP authorization spec.” If a guide teaches DCR first, it is out of date.
Finally, vendor names are not spec names. Claude Code supports four transports — stdio, HTTP, SSE and WebSocket — which is two more than the spec defines. Its config type field “accepts streamable-http as an alias for http. The MCP specification uses the name streamable-http for this transport.” Its docs add that “The SSE (Server-Sent Events) transport is deprecated. Use HTTP servers instead, where available”, and WebSocket is config-only: “The claude mcp add --transport flag doesn’t accept ws.” Scopes are host territory too — local (default, just you, this project; “Older versions called this scope project”), project (shared via .mcp.json), and user (all your projects; “Older versions called this scope global”). None of that is in the protocol. Config, scope and trust are host jobs.
Which one are you building?
One question decides it: do you own the LLM?
You own a model, a conversation and a UI, and you want to reach capabilities → you are building a host. The SDK will mint clients for you; you will rarely hand-write one. Your real work is the stuff the protocol explicitly refuses to do for you: consent, isolation, trust. Note the spec’s warning that “descriptions of tool behavior such as annotations should be considered untrusted, unless obtained from a trusted server”, echoed by Claude Code’s own docs: “Verify you trust each server before connecting it. Servers that fetch external content can expose you to prompt injection risk.”
You have a capability and want any AI app to reach it → build a server. This is roughly 90% of readers. Default to stdio, skip OAuth entirely (credentials from the environment, per the spec), and you are done in an afternoon. Start from how to build an MCP server, and browse what already exists before you write a duplicate.
You are writing the per-connection plumbing yourself — a test harness, a gateway, an eval rig, an agent framework → you are building a client. And here is the structural reason “mcp client python” autocompletes and then confuses everyone: the same SDK does both. There are ten official SDKs, formally tiered under the 2025-11-25 governance changes (SEP-1730) — Tier 1: TypeScript, Python, C#, Go; Tier 2: Java, Rust; Tier 3: Swift, Ruby, PHP, Kotlin — and “All SDKs support: Creating MCP servers that expose tools, resources, and prompts; Building MCP clients that can connect to any MCP server.” One import path, two roles. Of course people conflate them.
Sharpener: if you find yourself wanting to call an LLM from inside your server, you do not need to become a host. You need Sampling — ask the client’s model. Just remember it is deprecated as of 2026-07-28 with a twelve-month-minimum runway, so do not make it load-bearing today. If your real question is whether a server is even the right container for your logic, MCP vs Skills is the comparison you want.
Three housekeeping facts that keep articles honest. Governance: on 9 December 2025 Anthropic donated MCP to the Agentic AI Foundation, “a directed fund under the Linux Foundation, co-founded by Anthropic, Block and OpenAI, with support from Google, Microsoft, AWS, Cloudflare and Bloomberg.” It is not “Anthropic’s MCP” any more — though nothing changed operationally: “individual projects, such as MCP, maintain full autonomy over their technical direction and day-to-day operations” and “The governance model we introduced earlier this year continues as is.” The safest primary-sourced adoption claim is simply the AAIF platinum membership: “Amazon Web Services, Anthropic, Block, Bloomberg, Cloudflare, Google, Microsoft and OpenAI.” Scale: the donation post claims “Over 97 million monthly SDK downloads, 10,000 active servers and first-class client support across major AI platforms like ChatGPT, Claude, Cursor, Gemini, Microsoft Copilot, Visual Studio Code and many more” — December 2025 figures, roughly seven months stale now, so treat them as a floor. Reference servers: a widely repeated claim says the reference servers were archived. Half-true, and the half matters. modelcontextprotocol/servers is active, hosting seven reference implementations — Everything, Fetch, Filesystem, Git, Memory, Sequential Thinking, Time — framed as “educational examples for developers building their own MCP servers, not as production-ready solutions.” A separate repo, modelcontextprotocol/servers-archived, was archived on 29 May 2025 with fourteen entries and the blunt notice: “NO SECURITY GUARANTEES ARE PROVIDED FOR THESE ARCHIVED SERVERS.” Git genuinely appears on both lists. And the GitHub server people still cite as canonical is on the archived list — the live official one is github/github-mcp-server, GitHub’s own Go implementation, whose remote endpoint Claude Code’s docs wire up as claude mcp add --transport http github https://api.githubcopilot.com/mcp/. Use filesystem as your canonical example instead; the spec’s own docs do.
One last thing worth knowing before you go shopping: the official MCP Registry (announced 8 September 2025) is still in preview — “Breaking changes or data resets may occur before general availability” — it stores metadata only, pointing at npm/PyPI/Docker Hub packages under reverse-DNS names, and, contrary to a lot of advice, “The MCP Registry is not intended to be directly consumed by host applications. Instead, host applications should consume other MCP registries, such as downstream marketplaces, via a REST API conforming to the official MCP Registry’s OpenAPI spec.” Do not point your host at it directly; consume a downstream marketplace instead.
FAQ
What is the difference between an MCP client and an MCP server?
A client is a per-connection component inside a host that maintains a connection to exactly one server and pulls context back for the host to use. A server is a program that provides context and capabilities — tools, resources and prompts — with no model and no view of the conversation. The spec overview: hosts are “LLM applications that initiate connections”, clients are “Connectors within the host application”, servers are “Services that provide context and capabilities”. The client is not the app you see; it is the invisible connector the app spawns per server.
Is Claude Desktop an MCP client or an MCP host?
A host. The docs name Claude Code and Claude Desktop as hosts, and walk through Visual Studio Code as a host instantiating one client for the Sentry server and a second for the filesystem server. The host “coordinates and manages one or multiple MCP clients”. So “an MCP client for Claude” is a malformed request — what you actually want is a server; the host mints the client.
Can one MCP server have more than one client connected to it?
Yes. The 1:1 rule binds the connection, not the server. The spec says each client “communicates with exactly one server”, and the architecture overview in the MCP docs diagrams Client 3 and Client 4 both on remote Server C. Those docs add that remote Streamable HTTP servers “will typically serve many MCP clients” while local stdio servers “typically serve a single MCP client”. Writing “each server has exactly one client” inverts the spec.
Does the client always initiate every request in MCP?
Era-dependent. Under 2025-11-25 (current as of 17 July 2026), servers can send their own JSON-RPC requests to clients — sampling and elicitation over SSE. Under 2026-07-28, those interactions are “embedded as input requests inside an InputRequiredResult per MRTR, not delivered as separate requests”, so the client drives every round trip. Say which era you mean, or you will be wrong half the time.
Do I need OAuth to build an MCP server?
Only if it is remote. “Authorization is OPTIONAL for MCP implementations.” HTTP-transport implementations SHOULD conform to the authorization spec; stdio implementations “SHOULD NOT follow this specification, and instead retrieve credentials from the environment”. For remote it is OAuth 2.1: your server is the resource server, you MUST implement RFC 9728 Protected Resource Metadata, validate token audience, and you “MUST NOT pass through the token” you received from the client.
Does the 2026-07-28 spec revision break the client/server distinction?
No — it proves the distinction was never about the handshake. The RC removes initialize/initialized and Mcp-Session-Id, and the draft states “There is no negotiation handshake.” Yet the same draft architecture page still describes a “client-host-server architecture where each host can run multiple client instances”, with “each client having a 1:1 relationship with a particular server.” The roles survive verbatim. Only the mechanism most explainers used to define them retires.
DataLLM Lab