Claude Function Calling JSON Schema Example Guide
Claude's "function calling" — officially called tool use — lets the model call functions you define instead of just returning text. You describe each function as a JSON Schema object, Claude decides when to use it, and returns structured arguments you can parse and execute. This article walks through real, working JSON Schema examples for Claude's tool use, from a single simple tool to nested objects and multiple tools in one request.
The short answer: a Claude tool definition is a JSON object with name, description, and input_schema, where input_schema is standard JSON Schema (type: "object", properties, required). Claude reads the schema, decides whether to call the tool, and responds with a tool_use content block containing arguments that match that schema.
The Basic Shape of a Claude Tool
Every tool you pass to the Claude API follows this structure:
{
"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. 'Berlin'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
},
"required": ["city"]
}
}
A few things matter here:
descriptionfields are not optional in practice. Claude uses them to decide when and how to call the tool. A vague description leads to the tool being ignored or misused.requiredcontrols which fields Claude must fill in. Everything else is treated as optional.enumconstrains values tightly — useful for units, statuses, categories, or anything with a fixed set of valid inputs.
Full Request Example
Here's a complete request using the schema above against the Claude Messages API:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_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?" }
]
}'
If Claude decides to use the tool, the response contains a tool_use block like:
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": {
"city": "Lisbon",
"unit": "celsius"
}
}
Your code reads input, runs the actual logic (calling a weather API, querying a database, whatever the tool represents), and sends the result back as a tool_result message so Claude can finish its answer.
Nested Objects and Arrays
JSON Schema supports nesting, and Claude handles it well as long as the schema is well-formed. Here's a tool for creating a calendar event with a nested location object and an array of attendees:
{
"name": "create_event",
"description": "Create a calendar event with attendees",
"input_schema": {
"type": "object",
"properties": {
"title": { "type": "string" },
"start_time": {
"type": "string",
"description": "ISO 8601 datetime, e.g. 2024-06-01T14:00:00Z"
},
"location": {
"type": "object",
"properties": {
"name": { "type": "string" },
"address": { "type": "string" }
}
},
"attendees": {
"type": "array",
"items": { "type": "string" },
"description": "List of attendee email addresses"
}
},
"required": ["title", "start_time"]
}
}
Keep nesting shallow where possible. Deeply nested schemas (3+ levels) increase the chance Claude fills fields incorrectly or skips optional branches. If you need deep nesting, add explicit descriptions at every level, not just the top.
Multiple Tools in One Request
You can pass several tools in the same tools array. Claude picks whichever one fits the user's request, or none at all if the message doesn't need a tool:
"tools": [
{ "name": "get_weather", "description": "...", "input_schema": { ... } },
{ "name": "search_flights", "description": "...", "input_schema": { ... } },
{ "name": "convert_currency", "description": "...", "input_schema": { ... } }
]
If names and descriptions overlap conceptually, Claude can pick the wrong one. Keep tool names specific (search_flights rather than search) and make descriptions mention concrete trigger conditions, e.g. "Use this when the user asks about flight availability or prices between two cities."
Common Schema Mistakes
A few patterns consistently cause problems:
- Missing
required— Claude may omit fields you assumed were mandatory. - Using
type: "number"for values that should be integers — add"type": "integer"explicitly if decimals aren't valid. - No
enumon categorical fields — free text where you need"status": "open" | "closed"leads to inconsistent values like"Open"or"OPEN". - Ambiguous
descriptiontext — "The ID" tells Claude nothing; "The internal order ID, formatted as ORD-XXXXX" does.
Serving Tool Use Through an API Layer
If you're building a product on top of Claude's tool use rather than experimenting in a script, you'll eventually need to manage API keys per customer, track usage per tool call, and expose streaming — without reimplementing all of that yourself. SubToAPI wraps your existing Claude access into a standard HTTPS API with per-application keys (sub_live_...), so your JSON Schema tool definitions work exactly as shown above, just pointed at https://api.subtoapi.app/v1/messages with Authorization: Bearer $SUBTOAPI_KEY. Usage metadata and team seats are handled on the dashboard side, which is useful once tool use moves from a prototype into something customers depend on. See the tool use docs and quickstart for the exact request format.
Questions
Does Claude support the full JSON Schema spec for tools? No — Claude supports a practical subset: type, properties, required, enum, items, and description. Advanced keywords like oneOf, allOf, or $ref are not reliably supported and should be avoided in tool schemas.
Can Claude call multiple tools in a single response? Yes. Claude can return multiple tool_use blocks in one response if it determines several tools are needed to answer the request, and you send results back for each before Claude continues.
What happens if Claude's tool_use input doesn't match my schema exactly? In practice this is rare when descriptions are clear, but you should still validate input against your schema before executing it, since tool arguments come from a model and not a type-checked client.