What Tool Runner takes off your plate
Tool Runner (beta) moves the tool_use loop into the SDK itself: when Claude returns a tool_use block, the runner executes the matching tool, attaches the result as a tool_result, and sends the next request on its own. All seven SDKs — Python, TypeScript, C#, Go, Java, PHP, and Ruby — support it, and you cap turns with max_iterations (maxIterations / MaxIterations depending on language). When a tool function throws, the runner catches it and converts it into a tool_result with is_error: true, so the try/except-to-tool_result boilerplate that manual loops require on every call disappears.
What the defaults quietly drop
That automatic conversion loses detail. The tool_result only carries the exception's message (plus its type in Python) — the full stack trace is not included. SDK-level logging differs by language too: the ANTHROPIC_LOG environment variable turns on request/response logging only in Python, TypeScript, and Java; Go, Ruby, C#, and PHP never read it. The hook for inspecting a failed tool result before it reaches Claude — Python's generate_tool_call_response() and TypeScript's generateToolResponse() — exists only in those two SDKs. C#, Go, Java, and PHP give you no interception point there, so logging has to move inside the tool function itself.
Rollout to operations: a Tool Runner migration checklist
Treat migration as a per-tool decision, not a wholesale harness swap. Tools that need human approval — payments, deployments — stay on the manual loop; the official docs themselves recommend that path for human-in-the-loop approval or conditional execution. Set numeric targets before you touch code: removing 60% or more of the manual tool_use parsing and tool_result formatting code is a reasonable first goal, alongside a hard cap of max_iterations = 10 per turn and a token-retry ceiling that doubles max_tokens from 1024 up to a stop at 8192.
Failure pattern 1: mistaking the automatic is_error conversion in Go, Ruby, C#, or PHP for a logged failure. The fix is a standing rule that every tool function catches its own exception and logs it before returning or re-raising. Failure pattern 2: shipping a response that was cut off at max_tokens. The fix is to take over the runner's message history when stop_reason is max_tokens (append_messages() in Python, setMessagesParams() in TypeScript), double max_tokens, and retry up to the 8192 ceiling. Failure pattern 3: the state the runner appends every turn eventually overruns the context window — TypeScript's and Ruby's client-side compaction is already deprecated, so new integrations should default to server-side compaction through the context_management parameter instead.
Before shipping, the operations checklist should cover: classifying each tool as Tool-Runner-eligible or manual-loop-only based on whether it needs approval or conditional logic; confirming whether your SDK exposes a generate_tool_call_response-style interception hook, and if not, moving logging and masking inside the tool function; logging is_error rate, exception type, and turns-per-call explicitly, since the runner doesn't surface those by default; and treating PII masking as the tool's own responsibility, because the runner never filters tool output.
Run the improvement loop weekly. Pull the is_error rate and exception-type breakdown per tool; a tool with frequent parsing errors needs a better description or strict: true. The docs note Claude retries a corrected call 2-3 times when a parameter is missing — more retries than that is a signal the description or strict mode is overdue. Track how often you hit the max_iterations ceiling too, and decide each week whether to raise it or break the task into smaller steps.
Takeaways to apply now
Tool Runner cuts the repetitive code a manual loop requires, but it doesn't carry over exception visibility or your compaction strategy for free. Keep approval-gated tools on the manual loop, set numeric ceilings on max_iterations and max_tokens for everything else, and check whether your SDK exposes a failure-interception hook before you migrate — if it doesn't, move logging and masking into the tool function first. That order avoids most of the outages teams hit right after switching over.
References
Tool runner (SDK) — Claude Docs
Handle tool calls — Claude Docs
Ask AI about this article
The assistant has read this article. Ask anything — it answers from the text and says so when something isn't in it.
Loading the chat…