Building an MCP Client
The protocol is handled by every SDK, so the real work is translation: MCP tool definitions into your provider's format, namespacing two servers that both offer `search`, and deciding which tools the model sees at all.
The side that consumes rather than exposes
Building an MCP server covers writing the thing that offers capabilities. This is the other half: connecting to one, discovering what it has, and wiring its tools into your own model loop.
MCP host covers the roles and why one client per server is the isolation boundary. You write a client when your application is the host: an agent of your own that should reach MCP servers rather than tools you hardcoded.
The sequence
Four steps, and the shapes come from the data layer.
Connect over a transport. Transport layer covers stdio against streamable HTTP, and connect to a remote server covers what changes when it is not local.
Discover. Send server/discover to learn the server's supported versions,
capabilities and identity. Cache the response; it carries ttlMs and cacheScope
telling you for how long.
List with tools/list, resources/list or prompts/list. The response
carries the same caching fields, so re-listing on every turn is waste.
Call with tools/call, and put the result into your model loop.
Because the current revision is stateless, every request carries the protocol
version and your capabilities in _meta. There is no handshake whose result you
hold; there is a field you send each time.
Translating tool definitions is the real work
The interesting part is not the protocol. It is that MCP tool definitions and your model provider's tool format are not the same thing.
A tools/list response gives you name, title, description and
inputSchema. Your provider wants its own shape. So a client is largely a
translation layer in both directions: definitions out to the model, calls back to
the server, results into the conversation.
Two decisions land here.
Namespacing. Two servers can both offer search.
Building an MCP server names the
collision nobody plans for, and the client is where it has to be resolved, usually
by prefixing with the server.
Selection. Every definition you pass is input tokens on every request, and context failure modes records an 8b model failing with 46 tools and succeeding with 19 while the window was never the constraint. Passing everything from every connected server is the easy default and the wrong one. MCP host frames choosing which tools the model sees as a host responsibility, and this is where you implement it.
Subscribe only if you handle the gaps
Notifications are opt-in. You open a stream with subscriptions/listen, naming
the types you want, and the server acknowledges the subset it will honour.
Delivery is best effort, and notifications can be lost across a reconnect. So a client that refreshes its tool list only on notification will drift. Keep polling as a backstop and treat a notification as a prompt to refresh rather than as the truth.
Results are untrusted, and your client is where that is enforced
Both server articles here say it: server output is untrusted input to the model. The client is the component that puts it there.
That makes three things yours. Mark tool results as data rather than instructions when you add them to the conversation, which is the layer prompt injection says reduces rather than removes the risk. Cap result size before it reaches the window, because a tool returning three thousand rows fills the budget the next step needs. And decide whether a call requires user confirmation before you make it, since nothing in the protocol does that for you.
What you do not have to write
Every major SDK implements the protocol, so the connection, framing and message shapes are handled. What is left is what is specific to your application: the translation, the namespacing, the selection policy, the confirmation rules, and the loop.
If you find yourself parsing JSON-RPC by hand, reach for the SDK. If you find yourself writing the selection policy, that is the actual job.
Further reading
- Data layer:
server/discover, the*/listmethods, caching fields and subscriptions. - MCP host: the roles, and choosing which tools the model sees.
- Building an MCP server: the other side of the contract.
- Connect to a remote server: auth and trust once it is not local.
Knowledge check
Question 1 of 3
Sign in to save your progress and pick up where you left off.