Claude Tool Use: How It Works and How to Implement It
Claude tool use (also called function calling) lets Claude call external functions, APIs, or databases to complete tasks it can't do from its training data alone — checking live weather, querying your database, running calculations, or triggering actions in your app. Instead of just generating text, Claude decides when a tool is needed, tells you which tool to call and with what arguments, and then uses the result to finish its response.
This article covers how Claude tool use actually works under the hood, how to define and register tools, how to handle the request/response loop, and common patterns for building reliable tool-using applications.
How Claude Tool Use Works
Tool use in Claude follows a request-response loop, not a single call:
- You send a message along with a list of available
tools, each described with a name, description, and JSON schema for its inputs. - Claude analyzes the request. If it decides a tool is needed, it responds with a
stop_reasonoftool_useand a content block describing which tool to call and with what arguments. - Your application executes the actual tool (the API call, database query, calculation, etc.) — Claude never runs code itself.
- You send the tool's result back to Claude as a
tool_resultblock in a new message. - Claude uses that result to generate its final answer, or calls another tool if more steps are needed.
This loop is the core mental model: Claude proposes tool calls, your code executes them, and you feed results back. Claude never has direct access to your systems — it only sees what you send it.
Defining a Tool
Each tool needs a name, a clear description (this heavily influences whether and how Claude uses it), and an input schema in JSON Schema format:
{
"name": "get_stock_price",
"description": "Get the current price of a stock by ticker symbol.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Stock ticker symbol, e.g. AAPL"
}
},
"required": ["ticker"]
}
}
The description matters more than most people expect. Vague descriptions lead to Claude either not calling the tool when it should, or calling it with malformed arguments. Be explicit about what the tool does, what format inputs should be in, and any constraints.
A Full Request Example
Here's a complete round trip using curl against a Claude-compatible endpoint:
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": [{
"name": "get_stock_price",
"description": "Get the current price of a stock by ticker symbol.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {"type": "string"}
},
"required": ["ticker"]
}
}],
"messages": [
{"role": "user", "content": "What is the current price of AAPL?"}
]
}'
If Claude decides to use the tool, the response includes a tool_use content block:
{
"stop_reason": "tool_use",
"content": [
{
"type": "tool_use",
"id": "toolu_01A09q90qw",
"name": "get_stock_price",
"input": { "ticker": "AAPL" }
}
]
}
Your code executes the real lookup, then sends the result back as part of the conversation:
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: [/* same tools array */],
messages: [
{ role: "user", content: "What is the current price of AAPL?" },
{ role: "assistant", content: previousResponse.content },
{
role: "user",
content: [{
type: "tool_result",
tool_use_id: "toolu_01A09q90qw",
content: "AAPL: $231.45"
}]
}
]
})
});
Claude then generates a final answer using the tool result, without needing to see how the lookup was implemented.
Multi-Step and Parallel Tool Use
Claude can request multiple tools in a single turn (parallel calls) or chain several tool calls across turns to complete a multi-step task — for example, looking up a customer ID, then using that ID to fetch their order history. Your application loop needs to handle both cases: check stop_reason after every response, execute whatever tool_use blocks are present, and keep feeding results back until stop_reason becomes end_turn.
A robust implementation looks like a while loop rather than a fixed number of round trips, with a sane maximum iteration cap so a misbehaving tool schema can't cause an infinite loop.
Common Pitfalls
Overly broad tool descriptions. A tool named do_action with a one-line description gives Claude little to reason about. Be specific about inputs, outputs, and when the tool should (and shouldn't) be used.
Not validating tool inputs. Claude generates arguments based on the schema, but you should still validate and sanitize them before executing anything — especially for tools that touch databases or external APIs.
Forgetting to pass the same tools array on every follow-up call. The tools list needs to be included in every message in the loop, not just the first one.
Ignoring tool_choice. You can force Claude to use a specific tool, force it to use any tool, or let it decide automatically — useful when you know a particular step in your workflow always requires a tool call.
If you're building on Claude access through SubToAPI, tool use works the same way as calling Claude directly — the request/response shape is unchanged, so any existing tool-calling code migrates without modification. See the tool use docs and the messages reference for full schema details, or the quickstart if you're setting up your first integration.
questions
Does Claude execute the tool code itself? No. Claude only decides when to call a tool and generates the arguments. Your application is responsible for actually running the function, API call, or query and sending the result back.
Can Claude call multiple tools in one response? Yes. Claude can return several tool_use blocks in a single turn if the task requires it, and your code should execute each one and return all results before the next turn.
What happens if Claude misuses a tool or sends invalid input? You should validate all tool inputs before execution. If arguments are invalid, return an error message as the tool_result content — Claude will typically retry with corrected input based on that feedback.