Building an MCP Client

Advanced4 min

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.

#implementation

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

Knowledge check

Question 1 of 3

What is most of the work in writing an MCP client?

Sign in to save your progress and pick up where you left off.

Open this article on its own page