Skip to content

Building Clients and Hosts

So far this course has mostly looked at MCP from the server side: how to expose tools, resources, and prompts. This module flips the viewpoint. If you are building an AI application — an IDE assistant, a chat product, an autonomous agent — you are building a host, and inside that host you will create one client per server you connect to.

The distinction matters because the protocol assigns them different jobs:

  • A client is a protocol object. It speaks JSON-RPC to exactly one server over one transport, sends _meta on every request, and turns wire results into typed values.
  • A host is the application. It owns the language model, the conversation, the user interface, the consent decisions, and the registry that merges tools from many clients into one list the model can see.
flowchart LR
  subgraph host["Host (your AI app)"]
    llm["LLM loop"]
    ui["Consent UI"]
    reg["Tool registry"]
    c1["Client 1"]
    c2["Client 2"]
    c3["Client 3"]
  end
  s1["Server A (stdio, local)"]
  s2["Server B (HTTP, remote)"]
  s3["Server C (HTTP, remote)"]
  c1 --- s1
  c2 --- s2
  c3 --- s3
  reg --> llm
  ui --> llm
One host, many clients, one client per server

MCP is stateless: every request carries its protocol version, client capabilities, and client identity. There is still a good reason to keep one client object per server — each server has its own transport (a subprocess or an HTTP origin), its own discovered capabilities, its own credentials, and its own tool names. Bundling several servers behind one client would force the host to disambiguate all of that at every call. Keeping them separate lets the host treat a server as a unit it can connect, disconnect, and revoke independently.

The host is where the merging happens. It asks each client for tools/list, prefixes or otherwise namespaces the names to avoid collisions (two servers can both expose search), and hands the combined list to the model.

ConcernWhoWhy it lives there
Speaking JSON-RPC, adding _meta, parsing resultsClientPurely mechanical; one per transport
Deciding which server a tool call goes toHostOnly the host sees all servers
Calling the LLM and running the tool loopHostThe protocol never talks to a model
Asking the user before a destructive tool runsHostThe spec says a human should be able to deny any call
Rendering elicitation forms and URL consentHostIt owns the UI
Holding OAuth tokens and API keysHostCredentials must never reach the model or a sandbox
Caching tools/list by ttlMs, refreshing on list_changedHostCross-request policy
LessonYou will learn
Writing a ClientConnect over stdio or HTTP, list and call tools, read resources, answer elicitation
Wiring Tools to an LLMTurn tools/list into model tool definitions and run the agent loop
Using Servers in Real HostsClaude Code, Claude Desktop, the Claude API MCP connector, the Inspector
Scaling ToolsProgressive discovery, caching, and programmatic tool calling
Client Security DutiesWhat a well-behaved client must do to protect its user
How many MCP clients does a host typically create?
Which of these is the host responsible for, not the client?
Why does a host namespace tool names (for example server__tool)?