Three words come up in every MCP discussion — host, client, server — and they're used in a slightly unusual way, so candidates mix them up constantly. Get them right and the rest of the protocol becomes easy; get them wrong and every later answer sounds confused. This lesson pins them down, plus the message format and the handshake that decides what a connection can actually do.
The three roles
Host — the AI application a person actually uses: a desktop assistant, an IDE extension, an agent runtime. It holds the conversation, talks to the model, and — importantly — is where the human is, so it's where permission decisions happen.
Client — a connector inside the host that maintains a session with exactly one server. Three servers connected means three clients. This is the word people get wrong: a client is not a separate app, it's a component of the host.
Server — the process exposing a system's capabilities, published by whoever owns that system. It may run as a local subprocess or as a remote service (lesson 4).
Why one client per server?
It looks like unnecessary bookkeeping until you consider failure and trust. Each session is isolated: a server that crashes, hangs or behaves maliciously affects its own connection, not the others. The host can apply per-server policy — this one is read-only, that one needs confirmation before writes — and can revoke one connection without touching the rest.
Isolation per connection is a security feature, not an implementation detail — and it matters most when several servers of differing trustworthiness are connected at once.
The message format — deliberately boring
MCP uses JSON-RPC style messages: requests carrying an id, responses matching that id, and one-way notifications. Nothing exotic — which is exactly why SDKs in many languages appeared so quickly.
# request (client -> server)
{"jsonrpc": "2.0", "id": 7, "method": "tools/call",
"params": {"name": "get_order", "arguments": {"order_id": "8123"}}}
# response (server -> client)
{"jsonrpc": "2.0", "id": 7,
"result": {"content": [{"type": "text", "text": "status: out for delivery"}]}}
# notification (server -> client, no id, no reply expected)
{"jsonrpc": "2.0", "method": "notifications/resources/list_changed"}You rarely write these by hand — the SDK does — but recognizing the shape helps enormously when debugging (lesson 8), because a broken connection usually shows up as a malformed or unanswered message.
Wait — how does a client know what a server can do?
It asks, at connection time. Every MCP session opens with an initialization phase: client and server exchange protocol version and capabilities — does the server offer tools? resources? prompts? notifications? does the client support sampling? Only after both sides agree does normal operation begin.
Result
client → initialize (protocolVersion, client capabilities) server → initialize result (protocolVersion, server capabilities) client → notifications/initialized ── operation phase begins ──
After this, the client can list tools and the server knows what the client supports.
Two consequences worth knowing. First, this is what lets older clients talk to newer servers safely — nobody attempts a feature the other side didn't declare. Second, it's the source of the most common setup failure: a client showing zero tools usually means the capability was never declared or registration failed, not that the tools are broken.
The connection then ends with a shutdown phase, so the full lifecycle is initialization → operation → shutdown — three phases worth naming in an interview, because each produces a different class of bug.
Notifications — staying in sync without polling
Underlying systems change: new documents get indexed, a tool is added, a resource list grows. Rather than making clients poll, servers send notifications — one-way messages needing no response — so the client can refresh what it knows.
Useful, and worth flagging for later: it means your tool surface can change at runtime. For servers you control that's convenient; for third-party servers it's something to monitor (lesson 7).
Where trust lives — the host, always
A design question interviewers like: why does MCP put consent at the host rather than inside servers? Because only the host has a user. It can show a person what a server is asking to do and get a real answer; it can apply per-server policy; and it decides what actually enters the model's context.
Servers still validate their own inputs — they're services and should treat every call as untrusted — but consent needs a human, and the human is at the host. That single sentence answers a whole family of MCP security questions.
Selection-round radar: expect "explain the MCP architecture". Answer: host (the app, where the user is) creates one client per server; JSON-RPC messages over a transport; sessions begin with a version-and-capability handshake; notifications keep clients in sync; trust boundary at the host. Five sentences, complete.
Common mistakes
- Calling the server "the host" — the host is the AI application, not the machine running tools.
- Thinking one client multiplexes many servers — it's one client per server session.
- Skipping the capability handshake in a custom implementation, then hitting mysterious empty tool lists.
- Expecting servers to enforce user consent; that belongs at the host.
- Forgetting that notifications can change the tool surface at runtime.
Quick recap
| Concept | One-liner |
|---|---|
| Host | the AI application the person uses — holds the model and the consent |
| Client | a connector inside the host; exactly one per server session |
| Server | exposes one system's capabilities; published by that system's owner |
| Messages | JSON-RPC requests/responses plus one-way notifications |
| Lifecycle | initialization (version + capabilities) → operation → shutdown |
| Trust boundary | the host — it's where the user is |
Practice Zone — PYQs from real selection rounds
Six MCQs and a mapping task — identify host, clients and servers in a real developer setup, and say where consent belongs.
In MCP terminology, the host is:
Asked in

The relationship between clients and servers in MCP is:
Asked in

MCP messages are based on:
Asked in

What happens during the initialization phase of an MCP connection?
Asked in

Why does the security boundary sit at the host rather than inside servers?
Asked in

A server tells the client its resource list has changed. This is:
Asked in

Hands-on task:
A developer uses an AI coding assistant that can read their repo, query a staging database, and search internal docs. Identify the host, the clients and the servers — and say where the user's permission decisions belong.
Asked in

FAQ
Can one server serve several hosts at once?
A remote server can serve many hosts concurrently — that's the point of publishing one. A local stdio server is typically launched per host as a subprocess. Either way, each host↔server pair is its own session.
Does the model talk to servers directly?
No. The model emits a tool request; the host's client sends it to the server, executes nothing itself, and returns the result into the conversation. The propose/execute separation from ordinary tool calling is unchanged.
What happens if a server declares a capability it doesn't implement?
The client will attempt it and get an error — which is exactly why the handshake plus an inspector tool is the first stop when debugging. Declaring honestly is part of writing a well-behaved server.
Next lesson: the three primitives, and the control distinction interviewers ask about most — Lesson 3: Tools, Resources & Prompts →


