Claude API Agent Tool Chaining Example, Step by Step
Tool chaining is what turns a single Claude API call into an agent: the model picks a tool, you run it, you feed the result back, and Claude decides whether to call another tool or give a final answer. This article walks through a complete, working example — a research agent that looks up a stock ticker, fetches its price, then calculates a derived value — showing exactly how the request/response loop works and where developers usually get it wrong.
If you're searching for "claude api agent tool chaining example" you probably already know the basics of a single tool call and want to see what happens across multiple turns, including how to detect when the chain should stop, how to pass tool results back in the right format, and how to avoid infinite loops. That's what's covered below.
How tool chaining actually works
Claude's tool use follows a stateless request/response pattern — there's no server-side memory of "steps." Each turn you send the full conversation history, including every previous tool call and tool result, and Claude decides the next action based on that history. The chain is really just:
- Send messages + tool definitions.
- Claude responds with
stop_reason: "tool_use"and one or more tool call blocks. - You execute the tool(s) locally (or via your backend) and build
tool_resultblocks. - You append the assistant's tool_use message and your tool_result message to the conversation.
- Send the updated conversation again.
- Repeat until
stop_reasonis"end_turn"(Claude has a final answer) instead of"tool_use".
That loop — not any special "agent mode" — is the entire mechanism behind multi-step tool chaining.
Defining the tools
Here's a two-tool setup: one that looks up a ticker symbol, one that fetches a price.
[
{
"name": "lookup_ticker",
"description": "Look up the stock ticker symbol for a company name",
"input_schema": {
"type": "object",
"properties": {
"company": { "type": "string" }
},
"required": ["company"]
}
},
{
"name": "get_price",
"description": "Get the current price for a given ticker symbol",
"input_schema": {
"type": "object",
"properties": {
"ticker": { "type": "string" }
},
"required": ["ticker"]
}
}
]
The chaining loop in JavaScript
This example uses SubToAPI's endpoint, which accepts the same Messages-style payload, so the loop logic is identical to what you'd write against any Claude-compatible API.
const SUBTOAPI_KEY = process.env.SUBTOAPI_KEY;
const API_URL = "https://api.subtoapi.app/v1/messages";
const tools = [ /* lookup_ticker, get_price as above */ ];
async function callClaude(messages) {
const res = await fetch(API_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 1024,
tools,
messages
})
});
return res.json();
}
function runTool(name, input) {
if (name === "lookup_ticker") return { ticker: "ACME" };
if (name === "get_price") return { price: 142.17, currency: "USD" };
return { error: "unknown tool" };
}
async function agentLoop(userPrompt) {
let messages = [{ role: "user", content: userPrompt }];
const MAX_STEPS = 6;
for (let step = 0; step < MAX_STEPS; step++) {
const response = await callClaude(messages);
messages.push({ role: "assistant", content: response.content });
if (response.stop_reason !== "tool_use") {
return response.content; // final answer, chain ends
}
const toolResults = [];
for (const block of response.content) {
if (block.type === "tool_use") {
const output = runTool(block.name, block.input);
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(output)
});
}
}
messages.push({ role: "user", content: toolResults });
}
throw new Error("Max tool-chain steps exceeded");
}
Run agentLoop("What's the current price of Acme Corp stock?") and the model will typically: call lookup_ticker, receive the ticker, call get_price with that ticker, receive the price, then return a plain-text final answer — three network round trips chained automatically by the model's own decision-making, not by hardcoded branching in your code.
Why the MAX_STEPS guard matters
Agents chaining tools can loop longer than expected — a flaky tool result, an ambiguous prompt, or a tool that returns an error can cause Claude to retry or call another tool indefinitely. Always cap the number of turns and fail loudly rather than silently burning tokens. A sane default is 5–8 steps for most agent tasks; raise it only if you've profiled real conversations that legitimately need more.
Handling tool errors inside the chain
Don't just throw exceptions from runTool — feed the error back as a tool_result with an error flag so Claude can adapt:
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify({ error: "ticker not found" }),
is_error: true
});
Claude will often retry with corrected input or explain the failure to the user instead of crashing the whole chain — this is one of the more useful behaviors of tool-use models versus manually scripted pipelines.
Parallel tool calls in one turn
Claude can return multiple tool_use blocks in a single response when steps are independent (e.g., looking up two tickers at once). Your loop already handles this correctly above because it iterates over all blocks in response.content before sending results back — just make sure every tool_use_id gets a matching tool_result, or the next call will fail validation.
Running this against SubToAPI
If you're building this against SubToAPI instead of calling Anthropic directly, the request/response shape and tool-chaining logic above don't change — you're swapping the base URL and auth header. SubToAPI adds application-scoped sub_live_... keys, per-key usage metadata, and streaming support on top of the same Messages-style interface, which is useful once you have multiple agents or team members hitting the API and need to see who's consuming what. See /docs/tools for the full tool-use reference and /docs/streaming if you want to stream intermediate agent output to a UI. /docs/quickstart covers the initial setup if you haven't created a key yet.
questions
Do I need a special "agent" API or framework to chain tools with Claude? No. Tool chaining is just the standard Messages API called in a loop — you append tool_use and tool_result blocks to the conversation and resend until stop_reason is end_turn. Frameworks can simplify this but add no new capability.
How do I stop an agent from chaining tools forever? Set a hard step limit (e.g., 5–8 iterations) in your loop and throw or return gracefully if it's exceeded. Also check for repeated identical tool calls, which usually signal the model is stuck.
Can Claude call two tools in the same step? Yes — a single response can contain multiple tool_use blocks when the steps are independent. Loop over all of them and return a matching tool_result for each before sending the next request.