A turn is not a transaction: what breaks in a long chain of tool calls
•5 min read
When an assistant works through dozens of your tools in a single turn, there is no transaction around them. Everything before the failure has already happened, and the turn can still end by reporting success.
When an assistant works through dozens of your tools in one turn, the thing that breaks is not usually any single call: it is that there is no transaction around them. A failure at the end leaves everything before it done, on real data, with no rollback and often no record that the sequence was meant to continue. Designing for long chains means designing for that, because the assistant will not tell you it happened unless your tool responses give it the words.
145Tools this MCP server registers, across two source files
What does a failure halfway through actually leave behind?
A chain that fails at its last step leaves every earlier step standing. In a marketing tool set that can mean a draft created, a card drawn, a keyword tracked, a sitemap re-submitted and an announcement sent to five search engines, and then a publish refused. None of that unwinds. The right question when designing the tools is not how to roll it back but which order makes the surviving half safe on its own.
Market4's own publish path is arranged around exactly that. Publishing runs two announcements in one job — the sitemap re-submission and the IndexNow request — and neither is allowed to suppress the other, so a failure in one does not silence the other. The check that asks whether the published page really answers runs last, deliberately, because a diagnosis must not be able to delay or stop the announcements by being slow or by failing.
Five things that break in a long chain
A turn is not a transaction: what breaks in a long chain of tool calls
What goes wrong
What is already true when it does
What the tool response has to say
A guardrail refuses the last write
Everything the chain set up beforehand exists
What was refused, why, and which earlier artefact is now waiting
A rate limit stops one tool
Other tools are still answering normally
That this tool is limited, and when to try it again
An approval gate opens
The plan was made before the gate, and may have moved on
Which plan is waiting, priced, so approving it approves that plan
A result is too large to keep
Earlier results have been pushed out of the assistant's context
A summary plus a location, rather than the whole payload
A tool finds nothing
The chain reads it as a clean result and moves on
That it found nothing, distinct from could not look
Five outcomes an agent-facing tool can produce, and what an agent should do with each. The dangerous one is not failure; it is a response that describes a decision rather than the work.
Market4 turns one release note into a changelog page, a blog post, a mail-out and a week of social posts — and then tells you which of them brought anyone back.
No card to start. Cancel from the settings screen, not from an email.
can push everything earlier in the turn out of what the assistant is still holding, so by the last step what survives of step three may be no more than the assistant's own summary of it. That is how a chain ends with a confident account of work whose details nobody can now check.
Why the last call is the one to think about first
In a chain of tool calls, the calls that change the outside world should come after the calls that can refuse. A publish that can be refused for three separate reasons wants those three checks in front of it, run while the post is still a draft, so a refusal costs an edit rather than a retraction. That ordering is not politeness towards the model; it is what makes the failure cheap.
The same argument puts anything irreversible at the end and behind a person. Archiving a post removes it from every public read and it can never be edited again; cancelling a scheduled social post is a change of mind that happens after the conversation. Neither belongs in the middle of an automated sequence, and in Market4's own customer prompts those tools are deliberately not named at all, so an assistant reading the prompt never reaches for them.
What to build into each tool so a chain can survive
Make retries safe. A tool that spends money needs an idempotency key, so a repeated call after an unclear failure costs nothing twice.
Return the identifier of whatever was created, so a resumed chain can find it instead of making a second one.
Say what happened in words the assistant can act on: what failed, which parameter, what to do next.
Distinguish found nothing from could not look, because a chain treats a clean result as permission to continue.
Keep large payloads out of the response and hand back a location instead, so earlier steps stay readable.
Put the price in the approval request, because an approval without one is a guess about what is being agreed to.
None of this makes a long chain atomic, and nothing available in the protocol does. What it does is make each surviving half describable. A turn that ends with nine writes done and one refused is an acceptable outcome when the tenth response says exactly that; the same turn is a support ticket when the response says only that something went wrong.
Can I wrap several MCP tool calls in a transaction?
Not across tools. Each call is its own request and commits on its own, so there is no protocol-level way to undo the first nine when the tenth fails. What you can do is order the chain so the reversible and refusable work happens first, keep anything irreversible behind a human, and make every response describe what is now true. Atomicity, where you need it, belongs inside a single tool.
How many tools is too many for one server?
The limit is not the count but whether an assistant can choose correctly between them. Names that describe the job rather than the implementation, and descriptions that say when not to use a tool, matter more than the total. Clients also differ in how many tools they will expose at once, so a large set should degrade sensibly: the important reads should be findable without the assistant scanning everything.
What should a tool return when it partly succeeded?
Both halves, named. Say what was done, what was not, and what would have to happen for the rest to complete. A partial result reported as a failure hides work that actually landed, and reported as a success hides work that did not. The response is where a chain learns the difference, because nothing else in the turn is watching.
Does a rate limit belong per tool or per connection?
Per tool, when the tools differ in what they cost. A read that touches your own database and a call that spends a paid search quota are not the same risk, and one shared limit either throttles the cheap reads or fails to protect the expensive call. Per-tool limits also give the assistant a usable message: this specific tool is limited, the rest of the chain can continue.
A result too large to return has three cheaper fixes before persistence: send the delta instead of the state, a count instead of a list, and a budgeted answer that says it was budgeted.
The consumer of an MCP tool result is a model with a fixed budget for reading. Paging for that reader is a different design from paging for a web page.
McpApi designAi agents
A long chain of MCP tool calls is not a transaction