The Tool-Use Loop
When Claude asks to use a tool, run it, return the result, and let Claude continue.
Tool use is a small loop between your code and Claude.
The steps
- You send messages plus your tools.
- If Claude wants a tool, it stops with
stop_reason: "tool_use"and includes atool_useblock with the arguments. - You run the tool with those arguments.
- You append the assistant's message, then send a
tool_resultback. - Claude uses the result to write its final answer.
Example
let messages = [{ role: "user", content: "Weather in Paris?" }];
let res = await client.messages.create({ model: "claude-opus-4-8", max_tokens: 1024, tools, messages });
while (res.stop_reason === "tool_use") {
messages.push({ role: "assistant", content: res.content });
const results = [];
for (const block of res.content) {
if (block.type === "tool_use") {
const output = await runTool(block.name, block.input); // your function
results.push({ type: "tool_result", tool_use_id: block.id, content: output });
}
}
messages.push({ role: "user", content: results });
res = await client.messages.create({ model: "claude-opus-4-8", max_tokens: 1024, tools, messages });
}When to use it
- Run the full tool-use loop in a support bot so Claude can call lookupOrder, then craft its reply with the result.
- Implement a multi-step agent where Claude calls getProductDetails and then calculateShipping before answering.
- Build a React code assistant where Claude calls readFile, suggests changes, then calls writeFile with the new content.
More examples
Single-tool-use loop
Implements the three-step tool loop: send request, run the tool, return the result, get the final reply.
let messages = [{ role: "user", content: "What is the stock price of AAPL?" }];
// Step 1: initial request
let res = await client.messages.create({ model: "claude-haiku-4-5", max_tokens: 256, tools, messages });
if (res.stop_reason === "tool_use") {
const toolBlock = res.content.find(b => b.type === "tool_use");
// Step 2: run the tool
const result = await getStockPrice(toolBlock.input.ticker);
// Step 3: return result to Claude
messages = [
...messages,
{ role: "assistant", content: res.content },
{ role: "user", content: [{ type: "tool_result", tool_use_id: toolBlock.id, content: String(result) }] },
];
res = await client.messages.create({ model: "claude-haiku-4-5", max_tokens: 256, tools, messages });
}
console.log(res.content[0].text);Reusable runToolLoop helper
A reusable async function that drives the tool loop until Claude returns end_turn, handling multiple tools.
async function runToolLoop(client, params, handlers) {
let res = await client.messages.create(params);
while (res.stop_reason === "tool_use") {
const calls = res.content.filter(b => b.type === "tool_use");
const results = await Promise.all(calls.map(async c => ({
type: "tool_result",
tool_use_id: c.id,
content: String(await handlers[c.name](c.input)),
})));
params.messages = [
...params.messages,
{ role: "assistant", content: res.content },
{ role: "user", content: results },
];
res = await client.messages.create(params);
}
return res;
}Return a structured tool result
Returns a JSON-serialised object as the tool result so Claude receives rich structured data to base its answer on.
// After running your tool:
const toolResult = {
type: "tool_result",
tool_use_id: toolBlock.id,
content: JSON.stringify({ price: 189.45, currency: "USD", timestamp: Date.now() }),
};
messages.push(
{ role: "assistant", content: res.content },
{ role: "user", content: [toolResult] },
);
Discussion