Claude Function Calling: Multiple Tools Example
When you give Claude more than one tool in a single request, it has to decide which tool (or tools) to use, in what order, and with what arguments. This article walks through a concrete example — a weather tool and a currency conversion tool defined together — showing the request format, the response Claude returns, and how to handle the case where Claude calls both tools at once.
If you're looking for the short answer: you define multiple tools in the tools array of your API request, Claude picks zero, one, or several of them based on the user's message, and returns tool_use blocks you execute and feed back with tool_result. The rest of this post shows exactly what that looks like in practice.
Defining multiple tools in one request
Claude's tool use (function calling) works by passing a tools array where each entry has a name, description, and a JSON Schema input_schema. There's no special syntax for "multiple tools" — you just list more than one object.
[
{
"name": "get_weather",
"description": "Get current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
},
{
"name": "convert_currency",
"description": "Convert an amount from one currency to another",
"input_schema": {
"type": "object",
"properties": {
"amount": { "type": "number" },
"from": { "type": "string", "description": "ISO currency code" },
"to": { "type": "string", "description": "ISO currency code" }
},
"required": ["amount", "from", "to"]
}
}
]
The description field matters more than people expect. Claude uses it to decide relevance, so vague descriptions ("does math") lead to wrong tool selection when you have several similar tools registered.
A request that triggers both tools
Suppose the user asks: "What's the weather in Tokyo, and how much is 100 USD in JPY?" That single message needs both tools. Here's the request through SubToAPI's Messages endpoint (same shape as Anthropic's API, since SubToAPI proxies your Claude access):
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [ /* the two tool definitions above */ ],
"messages": [
{ "role": "user", "content": "Weather in Tokyo, and convert 100 USD to JPY." }
]
}'
Claude's response contains two tool_use blocks in the same content array — this is the "multiple tools" part of the question. It doesn't call one, wait, then call the other; it identifies both needs upfront and returns them together:
{
"content": [
{ "type": "text", "text": "I'll check both of those for you." },
{
"type": "tool_use",
"id": "toolu_01A",
"name": "get_weather",
"input": { "city": "Tokyo", "unit": "celsius" }
},
{
"type": "tool_use",
"id": "toolu_01B",
"name": "convert_currency",
"input": { "amount": 100, "from": "USD", "to": "JPY" }
}
],
"stop_reason": "tool_use"
}
Executing tools and sending results back
Your application code, not Claude, runs the actual functions. Loop over every tool_use block, execute the matching function, and return a tool_result for each one, matched by tool_use_id:
const toolResults = [];
for (const block of response.content) {
if (block.type !== "tool_use") continue;
let output;
if (block.name === "get_weather") {
output = await getWeather(block.input.city, block.input.unit);
} else if (block.name === "convert_currency") {
output = await convertCurrency(block.input.amount, block.input.from, block.input.to);
}
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(output)
});
}
Then send a follow-up message containing all the results as a single user message with role user and content set to the array of tool_result blocks. Claude reads both, composes a final natural-language answer, and returns stop_reason: "end_turn".
const followUp = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
tools,
messages: [
{ role: "user", content: "Weather in Tokyo, and convert 100 USD to JPY." },
{ role: "assistant", content: response.content },
{ role: "user", content: toolResults }
]
})
});
Handling tool selection when Claude doesn't need all of them
Not every message that includes multiple tools will trigger multiple calls. If the user only asks about weather, Claude returns a single tool_use block. Your code should always iterate over content rather than assuming a fixed number of tool calls — that's the pattern that breaks most integrations when someone adds a third tool later and the code still assumes exactly one or exactly two calls.
If you want to force a specific tool regardless of the message, use tool_choice with {"type": "tool", "name": "get_weather"}. Leave it as "auto" (the default) when you want Claude to decide among several tools, which is the normal setup for multi-tool agents.
Common mistakes with multiple tools
- Overlapping tool names or purposes. If
get_weatherandcheck_climateboth sound plausible for the same question, Claude sometimes picks the wrong one or calls both. Keep names and descriptions distinct. - Too many tools in one request. Beyond roughly 10–15 tools, selection accuracy drops. Group tools logically and only pass the subset relevant to the current conversation stage.
- Forgetting
tool_use_idmatching. Eachtool_resultmust reference the exactidfrom the correspondingtool_useblock — mismatches cause the API to reject the follow-up message. - Not handling streaming tool calls. If you're streaming responses, tool_use blocks arrive incrementally across
content_block_start/content_block_deltaevents — see /docs/streaming for the event shapes.
If you're running this through SubToAPI, the tool definitions and response format are identical to Anthropic's Messages API — see /docs/tools for the full schema reference and /docs/messages for request/response details. You get one API key, usage metadata per call, and team seats without changing any of the code above. Check /pricing for plan details or start a free trial at /signup.
questions
Can Claude call more than two tools in a single turn? Yes. There's no hard limit on how many tool_use blocks appear in one response — it depends on how many distinct actions the user's message actually requires and how many tools you've registered.
Does Claude run tools in parallel or sequentially? Claude decides what to call, but execution is up to your code. Since all tool_use blocks arrive in one response, you can execute the corresponding functions concurrently (e.g., with Promise.all) before sending results back.
What happens if I give Claude a tool it doesn't need? Nothing breaks — Claude simply won't generate a tool_use block for tools irrelevant to the message. Unused tools add slightly to prompt size but don't affect the final text response.