Claude API Tool Calling: A JSON Schema Example
If you're trying to get Claude to call a tool with the correct arguments, the fastest way to understand it is to see a complete, working JSON schema example alongside the actual request and response payloads. Below is exactly that: a real tool definition, a real request, and the tool_use block Claude returns, with explanations of why each field matters.
Tool calling in the Claude API works by describing a function as a JSON Schema object inside your request. Claude decides whether to call it, and if so, returns a structured tool_use content block with arguments that match your schema. You never execute code on Anthropic's servers — you send the schema, Claude returns arguments, your code runs the function and (optionally) sends the result back.
The Minimal Tool Schema
Here's a complete tool definition for a weather lookup function:
{
"name": "get_weather",
"description": "Get the current weather for a specific city and country.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city name, e.g. 'Berlin'"
},
"country": {
"type": "string",
"description": "ISO 3166 two-letter country code, e.g. 'DE'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit to return"
}
},
"required": ["city", "country"]
}
}
Three things matter here more than anything else:
descriptionon the tool itself tells Claude when to use it. Vague descriptions cause Claude to either over-call or ignore the tool entirely.descriptionon each property tells Claude how to fill in the argument. Don't skip these — they're where most schema bugs come from.requiredmust list every field the function genuinely needs. Ifunitis optional with a sensible default, leave it out ofrequired.
Full Request Example
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "Get the current weather for a specific city and country.",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "The city name" },
"country": { "type": "string", "description": "ISO 3166 code" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city", "country"]
}
}
],
"messages": [
{ "role": "user", "content": "What is the weather like in Lisbon right now?" }
]
}'
What Claude Returns
When Claude decides to use the tool, the response contains a tool_use block instead of (or alongside) plain text:
{
"id": "msg_01XYZ",
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01ABC",
"name": "get_weather",
"input": {
"city": "Lisbon",
"country": "PT",
"unit": "celsius"
}
}
],
"stop_reason": "tool_use"
}
Your application code reads input, calls the real weather API, and sends the result back as a tool_result message so Claude can produce a final natural-language answer:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01ABC",
"content": "18°C, partly cloudy"
}
]
}
That round trip — schema in, tool_use out, tool_result back in — is the entire pattern. Multi-tool setups and parallel tool calls follow the same structure, just with multiple tool_use blocks in one response.
Common JSON Schema Mistakes
A few issues show up repeatedly when people build their first tool:
- Nested objects without descriptions. If an argument is itself an object (e.g.
address: { street, city }), describe the nested properties too. Claude can't infer what you mean from field names alone. - Using
enumfor free text. Only useenumwhen the set of valid values is genuinely fixed. Forcing an open-ended field into an enum causes Claude to pick the nearest match instead of the real value. - Over-nesting arrays of objects. It works, but descriptions need to be extremely explicit, or Claude will leave optional sub-fields empty inconsistently.
- Forgetting
additionalProperties: falsewhen you need strict validation — Claude generally respects schemas well, but a stray extra key can break strict downstream parsers. - Making everything required. If a field has a sensible default, mark it optional and handle the default in your own code.
Validating Before You Ship
Treat your tool schema like an API contract. Run it through a JSON Schema validator before deploying, and test it with adversarial prompts — ambiguous requests, missing information, multiple tools that could plausibly apply. This catches most of the "Claude picked the wrong tool" issues before they reach production.
Where SubToAPI Fits In
If you're building tool-calling features into a product, SubToAPI gives you an HTTPS endpoint on top of your existing Claude access, with sub_live_... application keys, streaming, and usage metadata per key — useful when different teams or environments need isolated tool definitions and separate rate limits. The same tools array and request shape shown above works against SubToAPI's endpoint; see the tool use docs and messages API reference for details, or check pricing if you're evaluating it for a team.
FAQ
Does Claude support standard JSON Schema, or a custom format? Claude's input_schema is a subset of standard JSON Schema (draft 2020-12 style). Most common keywords — type, properties, required, enum, items, nested object — work as expected, but exotic keywords like $ref across files aren't reliably supported.
Can Claude call multiple tools in one response? Yes. If the model determines multiple tool calls are needed, the response content array can contain multiple tool_use blocks, each with its own id you match back when sending tool_result messages.
What happens if the arguments don't match my schema? Claude generally produces arguments that conform to your schema, but you should still validate on your end — type mismatches or missing required fields can occasionally slip through, especially with vague descriptions or ambiguous enums.