An agent rarely finishes a real task in one tool call. It looks something up, then acts on what it found — search for a customer, then update their record; list open invoices, then send one. Whether that second call actually works depends entirely on what the first call handed back. MCP has no built-in mechanism for linking one tool’s output to another tool’s input — that link is something you, the server author, have to design on purpose. Here’s what that design actually involves.
The protocol doesn’t do this for you
A tool definition describes its own inputSchema and, optionally, its own outputSchema (see our piece on MCP structured output). Neither one references any other tool. There’s no field that says “the customer_id this tool returns is the same shape the update_customer tool expects as input” — the model has to notice that on its own, from field names and a plausible guess, every time. If you want calls to actually chain reliably, you’re building that connective tissue yourself, not getting it from the spec.
Echo back exactly what the next call will need
The most common failure is a tool that returns enough for a human to read but not enough for the next tool to act on. A search tool that returns “Acme Corp — Berlin office” as a single string is fine for display and useless as input to anything else. The fix is almost mechanical once you notice it: every identifier a later call could plausibly need — a record ID, an order number, a resource URI — should come back as its own field, named the same way the tool that consumes it expects to receive it. If search_customers returns customer_id and update_customer expects id, that mismatch is small enough that a model usually bridges it correctly, and large enough that it sometimes doesn’t. Matching names across tools on the same server costs nothing and removes a whole category of silent failure.
Don’t make the next call guess at an opaque blob
Some APIs hand back a composite object — a nested structure, an encoded cursor, a reference token — that only makes sense to the backend that issued it. That’s fine as a value passed straight back into the next call, and it’s a problem the moment the model is expected to read it, decide something based on its contents, or construct it by hand. If the only valid use of a value is “copy it verbatim into the next tool call,” say so in the field’s description. If the model actually needs to reason about what’s inside it — is this invoice overdue, is this page the last one — pull that fact out into its own plain field instead of leaving it buried in a token the model has no way to parse correctly.
Pagination cursors are the clearest version of this problem
MCP’s own list operations (tools/list, resources/list) use a cursor the client passes back unexamined, and that pattern is worth copying for your own tools, because the alternative is worse. We covered the gap directly in how to paginate large MCP tool results: the protocol gives you cursor-based paging for a server’s own tool and resource lists, and nothing for a single tool call whose result is too big. If you build your own cursor for a search or list tool, treat it exactly like the protocol treats its own — an opaque value the model passes straight through — and say that explicitly in the tool description, or you’ll get a model trying to construct or edit a cursor it was never meant to touch.
A fused tool sidesteps the problem — sometimes
One way to avoid output-to-input mismatches entirely is to not expose the intermediate step at all. Our piece on designing MCP tools around jobs, not endpoints covers the tradeoff in more depth, but it applies directly here: a single find_and_update_customer tool has no chaining problem, because there’s no handoff between two separate calls for anything to go wrong in. That’s a real option, not just a consolation prize — but it only works when the two steps are always done together. The moment an agent sometimes needs to look something up without acting on it, you’re back to two tools, and back to designing the handoff between them properly.
Retries make this worse, not better
A chain adds a second place for a retry to go wrong. If step one succeeds but the response is lost or delayed, a client may retry it, and now a model holding two different IDs for what should be the same record has no way to know they’re duplicates. This is the same ground our guide to MCP tool idempotency covers for a single tool — it compounds across a chain, because an inconsistent ID from a retried first call can quietly poison every call that follows it. When a second call in a chain fails, the error your tool returns should say plainly which input it rejected and why, not just that something went wrong — see how to handle MCP tool errors for what actually helps a model recover instead of guessing at a fix.
The bottom line
MCP gives you a schema for one tool’s input and, optionally, one tool’s output — nothing that connects the two across a chain. If your server’s tools are meant to be used together, that connection is yours to build: matching field names, explicit opaque-value handling, and error messages that name what went wrong. Browse gate’s MCP server directory with this in mind and the difference is visible from the outside — a server whose tools were designed to chain reads differently than one that only ever got tested one call at a time.