Claude Tool Use: A Function Calling Tutorial
Claude tool use (also called function calling) lets Claude call functions in your codebase instead of just generating text. You describe the tools available — their names, purposes, and input schemas — and Claude decides when to invoke them, producing structured JSON arguments you execute on your side. This tutorial walks through the full request/response cycle so you can wire it into a real application.
If you're searching for how this actually works end to end, here's the short version: you send a request with a tools array, Claude replies with a tool_use block containing a function name and arguments, your code runs that function, and you send the result back in a follow-up message so Claude can continue the conversation. The rest of this guide covers each step in detail, with working code.
How Claude Tool Use Works
Tool use in Claude follows a loop, not a single call:
- You send a message with a list of tool definitions (name, description, JSON schema for inputs).
- Claude's response may include a
tool_usecontent block instead of (or alongside) plain text, specifying which tool to call and with what arguments. - Your application executes the actual function — hitting a database, calling an external API, running a calculation.
- You send the result back to Claude as a
tool_resultblock in the next message. - Claude incorporates the result and produces a final answer, or calls another tool if needed.
This loop is what makes Claude useful for agentic tasks: looking up live data, performing calculations it can't do reliably in text, or triggering actions like sending an email or creating a ticket.
Step 1: Define Your Tool
Each tool needs a name, a description (Claude uses this to decide when to call it), and an input_schema in JSON Schema format:
{
"name": "get_weather",
"description": "Get the current weather for a given city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name, e.g. Paris" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
Write descriptions as if explaining the tool to a new teammate. Vague descriptions ("gets data") lead to Claude either skipping the tool or calling it at the wrong time.
Step 2: Send the Request with Tools Attached
Here's a request using curl against a generic Claude-compatible Messages endpoint:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-3-5-sonnet-20241022",
"max_tokens": 1024,
"tools": [{
"name": "get_weather",
"description": "Get the current weather for a given city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}],
"messages": [{ "role": "user", "content": "What is the weather in Lisbon?" }]
}'
Step 3: Handle the Tool Use Response
Claude's reply won't contain a final answer yet — it contains a tool call:
{
"content": [
{
"type": "tool_use",
"id": "toolu_01abc",
"name": "get_weather",
"input": { "city": "Lisbon" }
}
],
"stop_reason": "tool_use"
}
Check stop_reason — if it's tool_use, your application must act before anything else happens. This is the most common thing beginners miss: nothing runs automatically. You parse the tool_use block, call your real get_weather function, and move to the next step.
Step 4: Send the Tool Result Back
You append both the assistant's tool call and your tool's output to the conversation, then send it again:
const followUp = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-3-5-sonnet-20241022",
max_tokens: 1024,
tools: [ /* same tool definitions as before */ ],
messages: [
{ role: "user", content: "What is the weather in Lisbon?" },
{ role: "assistant", content: previousAssistantContent },
{
role: "user",
content: [{
type: "tool_result",
tool_use_id: "toolu_01abc",
content: "18°C, partly cloudy",
}],
},
],
}),
});
Claude now generates a final text response using the real weather data. If your tool fails, send an error string in the tool_result and Claude will typically explain the failure to the user rather than crashing the flow.
Common Mistakes to Avoid
- Letting the model "guess" when no tool fits. If none of your tools match the user's request, Claude should answer in plain text — don't force every message through the tool loop.
- Skipping validation. Claude's JSON output is usually well-formed, but always validate
inputagainst your schema before executing, especially if the tool touches a database or external system. - Forgetting multi-step calls. Some tasks require two or three tool calls in sequence (look something up, then act on it). Keep looping until
stop_reasonis no longertool_use. - Overloading one tool with multiple responsibilities. Smaller, well-named tools with tight schemas get called more reliably than one giant multi-purpose function.
Running This in Production
Once tool use works in a script, the next problem is usually operational: API keys per environment, rate limits, streaming tool calls to a frontend, and giving teammates their own access without sharing one shared key. SubToAPI turns your existing Claude access into an HTTPS API with per-application keys (sub_live_...), so you can issue separate keys for staging and production, see usage per key, and manage team seats without passing around a single credential. The request format for tool use is the same Messages-style payload — see the tools docs for the exact schema SubToAPI expects, streaming if you want partial tool-call output as it's generated, and the quickstart to get a key from the pricing page in a few minutes.
Questions
Does Claude execute the function itself? No. Claude only decides that a function should be called and what arguments to use. Your application is responsible for actually running the function and returning the result.
Can Claude call multiple tools in one turn? Yes, a single response can include several tool_use blocks if the task requires calling more than one tool before continuing, and you return all corresponding tool_result blocks together in the next message.
What happens if the tool call fails? Return an error message as the tool_result content instead of throwing an exception in your own code. Claude will read the error and typically explain the issue to the user or try an alternative approach.