Claude API Function Calling with Multiple Tools
When you register more than one tool with the Claude API, Claude decides on its own which tool (or tools) to call based on the conversation and the schemas you provide. Function calling with multiple tools isn't a special mode you switch on — it's the same tools array and tool_use content block mechanism as single-tool calling, just with more entries in the array and more careful handling on your end.
The practical challenge isn't registering the tools — it's writing schemas Claude can reliably tell apart, handling the case where Claude wants to call several tools in one turn, and routing each tool_use block to the correct handler before sending results back. This article covers all three.
How multiple tools work in a single request
You pass an array of tool definitions in the tools parameter of your Messages API call. Each tool has a name, a description, and an input_schema (JSON Schema). Claude reads the user's message, looks at every tool description, and decides whether to respond with plain text, call one tool, or call several tools in the same response.
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "Get current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
},
{
"name": "get_flight_status",
"description": "Get the status of a flight by flight number",
"input_schema": {
"type": "object",
"properties": {
"flight_number": { "type": "string" }
},
"required": ["flight_number"]
}
}
],
"messages": [
{ "role": "user", "content": "Is it raining in Berlin, and is flight LH123 on time?" }
]
}
For this prompt, Claude will typically return two tool_use blocks in a single assistant message — one for get_weather, one for get_flight_status. This is the core thing to design around: with multiple tools available, your response handling must expect an array of tool calls, not just one.
Handling a response with several tool_use blocks
The assistant message's content is an array. When multiple tools are invoked, you'll see multiple tool_use objects mixed in with any text blocks. You need to loop over all of them, execute the corresponding function, and return a tool_result for each one — matched by tool_use_id — in a single follow-up user message.
const response = await client.messages.create({
model: "claude-sonnet-4-5",
max_tokens: 1024,
tools,
messages,
});
const toolResults = [];
for (const block of response.content) {
if (block.type === "tool_use") {
const result = await runTool(block.name, block.input);
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(result),
});
}
}
messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });
A common bug is sending only one tool_result back when Claude asked for two. If any tool_use block is left without a matching result, the next request will fail or Claude will get confused about what happened. Always iterate over the full content array and answer every tool_use block, even if one of your functions errors out — in that case, return a tool_result with an error message rather than nothing.
Writing schemas that don't collide
With one tool, ambiguity rarely matters. With five or ten tools, vague names and descriptions cause Claude to pick the wrong one or call two tools when one would do. A few rules that hold up in practice:
- Name tools by action + object, not by internal system name.
get_user_by_emailbeatslookupUser2. - Write descriptions that state when to use the tool, not just what it does — "Use this when the user asks about order status, not shipping cost" prevents overlap with a
get_shipping_costtool. - Keep required fields minimal. Every required field is a chance for Claude to under-specify and trigger a schema validation failure.
- Avoid near-duplicate tools. If two tools do almost the same thing with slightly different parameters, merge them into one tool with an optional parameter instead.
If you're new to schema design specifically, our function calling with JSON Schema guide covers the structure in more depth; this article focuses on the multi-tool orchestration layer on top of it.
Forcing or restricting tool choice
Sometimes you want Claude to always use a specific tool, or to choose freely among a subset. The tool_choice parameter controls this:
{"type": "auto"}— default, Claude decides whether and which tools to use.{"type": "any"}— Claude must call one of the provided tools.{"type": "tool", "name": "get_weather"}— Claude must call this specific tool.
This matters more as your tool count grows: forcing any on a request where you know a tool call is required (e.g., a structured extraction step) avoids Claude replying with plain text when you specifically need a tool_use block back.
Where a proxy layer helps
None of this logic changes if you're calling Claude directly or through a gateway, but a few things get easier with a thin API layer in front of your integration. SubToAPI turns your existing Claude access into a standard HTTPS API — sub_live_... keys, the same Messages-style request shape, and usage metadata per key — so you can run this multi-tool loop from a backend, share access across a team without sharing raw credentials, and see which key is generating the tool-call traffic.
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": [ ... ],
"messages": [ { "role": "user", "content": "..." } ]
}'
If you're building this for a team rather than a single script, the multi-tool loop above stays identical — only the key management and per-seat visibility change. See the quickstart and messages docs for the full request/response shape, and pricing if you want seats for a team rather than a single key.
questions
Can Claude call more than two tools in one response? Yes. There's no hard limit on how many tool_use blocks can appear in a single assistant message beyond the model's own judgment about the task — three or four in one turn is common for multi-step requests.
Does adding more tools slow down responses or hurt accuracy? Adding tools increases prompt size slightly (schemas count as input tokens) and can reduce selection accuracy if descriptions overlap. Keep tool count focused on what's relevant per use case and write distinguishing descriptions to keep accuracy high.
What happens if I don't return a tool_result for every tool_use block? The next API call will either error out or produce an assistant message that ignores the missing result, since Claude expects a result for each tool it invoked. Always match every tool_use_id with a corresponding tool_result before continuing the conversation.