Claude API Tool Definition Schema Format, Explained
When you give Claude access to tools, you're not writing a function — you're writing a JSON object that describes a function well enough that a language model can decide when and how to call it. The Claude API tool definition schema format follows JSON Schema conventions, wrapped in a specific structure that the API expects in the tools array of a request. Get the shape wrong and Claude either ignores the tool, calls it with malformed arguments, or the API rejects the request outright.
This article walks through the exact format, field by field, with working examples and the mistakes that cause the most confusion.
The basic shape
Every tool definition is a JSON object with three top-level keys: name, description, and input_schema. Here's the minimal valid structure:
{
"name": "get_weather",
"description": "Get the current weather for a given city.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city name, e.g. 'Berlin'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["city"]
}
}
You pass an array of these objects in the tools field of your request body, alongside model, messages, and max_tokens. No separate endpoint, no registration step — tools are defined per-request.
Field-by-field breakdown
name must be a string, typically snake_case, unique within the request. Keep it short and descriptive — Claude uses this name both to decide which tool to call and to label the tool_use block it returns. Avoid spaces or special characters; stick to letters, numbers, and underscores.
description is the single most important field for getting good tool-selection behavior. This is natural language text that Claude reads to decide when to use the tool. Vague descriptions like "gets data" produce unreliable calling behavior. Specific descriptions that state what the tool does, what it returns, and any constraints work much better:
"Get the current weather conditions for a city. Returns temperature,
conditions, and humidity. Only works for cities with a known weather
station — does not support arbitrary addresses."
input_schema is a JSON Schema object (specifically a subset: object type at the root, with properties and required). This is where most formatting mistakes happen. Common supported types inside properties:
string— optionally withenumfor a fixed set of valuesnumber/integerbooleanarray— requires anitemsschemaobject— nested objects with their ownproperties
Each property should have its own description too. Claude uses per-field descriptions to fill in values correctly, especially for enums or formatted strings like dates.
A more realistic example
Here's a tool with a nested array and an enum, representative of what you'd actually ship:
{
"name": "search_orders",
"description": "Search customer orders by status and date range. Returns a list of matching order IDs with totals.",
"input_schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["pending", "shipped", "delivered", "cancelled"],
"description": "Order status to filter by"
},
"date_from": {
"type": "string",
"description": "ISO 8601 date, e.g. 2024-01-01"
},
"limit": {
"type": "integer",
"description": "Max number of results to return, default 20"
}
},
"required": ["status"]
}
}
How Claude responds when it calls a tool
When Claude decides to use a tool, the response content includes a tool_use block instead of (or alongside) plain text:
{
"type": "tool_use",
"id": "toolu_01A2...",
"name": "search_orders",
"input": {
"status": "shipped",
"date_from": "2024-03-01"
}
}
Your application executes the actual function, then sends the result back as a tool_result block in the next message, referencing the same id. Claude then incorporates that result into its final answer. This request/response loop — definition, call, result, continuation — is the core mechanic of tool use, and it's the same whether you're calling Anthropic directly or through a proxy layer.
Common mistakes
Missing required array. If a field is mandatory, list it in required. Without this, Claude may omit it, and your function call will fail on your end, not on Anthropic's.
Over-nesting without descriptions. Nested objects and arrays work, but every level needs its own description if you want reliable extraction. A bare "type": "object" with no description is a black box to the model.
Using unsupported JSON Schema features. Things like oneOf, anyOf, $ref, or regex pattern validation are not reliably supported. Stick to type, properties, required, enum, and items — the well-tested subset.
Too many tools in one request. There's no hard limit, but past a dozen or so well-described tools, selection accuracy tends to degrade. Group related tools or split logic across turns if you have a large toolkit.
Ambiguous names across tools. get_data and fetch_data in the same request confuses tool selection. Prefix by domain if you have many tools: orders_search, orders_cancel, inventory_check.
If you're building on top of Claude's tool-calling through SubToAPI, the format is identical — you send the same tools array with name, description, and input_schema to https://api.subtoapi.app/v1/messages with your sub_live_... key, and tool_use/tool_result blocks round-trip exactly as documented. See /docs/tools for the full reference and /docs/messages for how tool blocks fit into the message format.
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_weather",
"description": "Get current weather for a city.",
"input_schema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}],
"messages": [{ "role": "user", "content": "What is the weather in Lisbon?" }]
}'
If you want to test schemas quickly without wiring up a backend, /signup gives you a trial key and the /docs/quickstart guide gets a first tool-calling request running in a few minutes.
questions
Does the tool schema support full JSON Schema? No. It supports a practical subset — type, properties, required, enum, items, and nested objects/arrays. Features like $ref, oneOf, and pattern are not reliably honored and should be avoided.
Can I define tools without a required field? Yes, but then every property is treated as optional, and Claude may omit fields you actually need. Explicitly list required fields to get consistent function calls.
What happens if the schema is invalid JSON? The API returns a validation error before the model ever sees the request. Validate your tool definitions with a JSON Schema linter before sending, especially if they're generated programmatically.