MCP Fundamentals: Servers, Clients, Hosts
The core MCP vocabulary — server, client, host — and how these three roles connect in a running system.
The Model Context Protocol (MCP) is an open protocol that standardizes how AI applications connect to external tools, data sources, and prompt templates. Before MCP, every integration between a model-powered app and, say, a database or a ticketing system was a bespoke, one-off integration. MCP defines a common wire protocol so that an integration written once as an MCP server can be used by any MCP-compatible application without custom glue code.
The three roles
MCP defines three distinct roles, and the exam is precise about which word means which role.
- Server: the thing that exposes capabilities — tools, resources, and/or prompts — over the protocol. A server wraps some external system: a GitHub server exposes GitHub operations, a Postgres server exposes database queries, a filesystem server exposes file operations. The server owns the implementation details of talking to that external system.
- Client: maintains a 1:1 connection to exactly one server. A client is not a general-purpose consumer of many servers at once — each client instance is scoped to a single server connection, handling that connection's session, capability negotiation, and message routing.
- Host: the application the user actually interacts with — Claude Code, Claude Desktop, or a custom agent application. The host coordinates multiple clients simultaneously, one per connected server, and is what decides which servers to connect to, presents available tools to the model, and enforces permissioning around what the model is allowed to call.
So a single host, like Claude Code, might run three clients concurrently, each one connected 1:1 to a different server (a GitHub server, a Postgres server, and a Slack server). The host aggregates what all three clients expose and presents a unified surface to the model and the user.
Why the 1:1 constraint matters
The client-per-server design keeps each connection's protocol state (capability negotiation, session lifecycle, subscriptions to resource updates) isolated and simple. It also means adding a new server to a host is additive — you spin up one more client — rather than requiring changes to how existing connections are managed.
Capability negotiation and lifecycle
When a client first connects to a server, the two sides perform a handshake in which the server advertises which primitives it supports — does it expose Tools, Resources, Prompts, some combination, or none — along with protocol version information. The host uses this to decide what to surface to the model and the user for that particular connection; a server that only exposes Resources, for instance, will never appear as a callable tool no matter how the model is prompted. This negotiation happens once per connection, at startup, not per-request, which is part of why the client is scoped to a single server: the negotiated state belongs to that one relationship and doesn't generalize across servers with different capabilities.
It is also useful to keep the three roles distinct when reasoning about failure. If a server crashes, only the one client connected to it is affected — the host and its other clients keep functioning, and the host is responsible for deciding whether to surface that degraded state to the user (for example, showing that one server's tools are temporarily unavailable) rather than failing the whole session. That resilience story only works because responsibilities are partitioned the way they are: server owns the external system, client owns one connection's state, host owns orchestration across all of them.
Scenario: a team wants Claude Code to be able to query their internal analytics database and also create Jira tickets. The correct architecture is one MCP server for the analytics database and a separate MCP server for Jira, with Claude Code (the host) running two clients, one per server. It would be architecturally wrong to try to build a single combined "analytics-and-jira" server just to avoid running two clients — servers should be scoped to one external system, and the host is designed to handle multiple clients without added complexity.
Try it
In Claude Code, run the command to list configured MCP servers (check the CLI's MCP-related slash command or config file) and note how many servers are connected. For each one, identify what single external system it wraps. If you have none configured, add one lightweight server (a filesystem or fetch server is a common starting point) and observe how it appears as a distinct connection separate from any others you add later.