Claude API Function Calling Schema Design Guide
Designing a function calling schema for the Claude API means writing a tools definition that Claude can parse unambiguously and that your backend can validate without guesswork. The core answer: keep each tool narrow, name fields exactly like your code expects them, describe every parameter in plain language, and make required fields explicit rather than relying on defaults.
If you're here because Claude keeps calling the wrong tool, omitting arguments, or returning malformed JSON, the fix is almost always in the schema — not in the prompt. Claude's tool use is driven by the input_schema you provide, and small ambiguities compound fast once you have more than two or three tools registered.
How Claude API Tool Schemas Actually Work
When you send a request with tools defined, Claude evaluates the user's message against each tool's name, description, and input_schema, then decides whether to respond with text or a tool_use block containing structured arguments. The schema format follows JSON Schema conventions:
{
"name": "get_order_status",
"description": "Retrieve the current status of a customer order by its ID.",
"input_schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The unique order identifier, e.g. ORD-2024-00913"
}
},
"required": ["order_id"]
}
}
Claude doesn't execute the function — it only produces the arguments. Your application reads the tool_use block, runs the real logic, and sends the result back in a tool_result block on the next turn. That round trip is where schema quality matters most: if the arguments are wrong or incomplete, the error shows up in your own code, not in Claude's response.
Naming and Structuring Tools
One tool, one responsibility. Resist the urge to build a single manage_data tool with a mode field that switches between create, update, and delete. Claude picks tools based on semantic matching between the description and the user's intent — a generic tool with an internal mode parameter is harder for the model to route correctly than three specific tools: create_record, update_record, delete_record.
Names should match your API, not your conversation. Use search_products, not find_me_something. Claude reads the name as a signal, but your code will also pattern-match on it, so keep it consistent with your actual function or endpoint name.
Descriptions carry more weight than people expect. Write descriptions the way you'd explain the function to a new engineer: what it does, when to use it, and when not to use it. For tools with overlapping use cases, explicitly state the distinction:
{
"name": "refund_order",
"description": "Issue a full or partial refund for an already-paid order. Do not use this for order cancellations before payment — use cancel_order instead."
}
Designing the input_schema
Stick to flat, predictable structures wherever possible:
- Use
string,number,boolean,array, and simple nestedobjecttypes. - Avoid deeply nested objects (more than two levels) — Claude is more likely to drop or misplace fields in deep structures.
- Use
enumfor constrained values instead of free-text descriptions. This single change eliminates most "invalid category" validation errors. - Mark fields
requiredexplicitly. Claude will usually omit optional fields rather than guess, so don't rely on it filling in sensible defaults.
Example with an enum and a nested array:
{
"name": "create_support_ticket",
"description": "Open a new support ticket for a customer issue.",
"input_schema": {
"type": "object",
"properties": {
"priority": {
"type": "string",
"enum": ["low", "normal", "high", "urgent"],
"description": "Ticket priority based on business impact."
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Short keywords categorizing the issue, e.g. ['billing', 'refund']"
},
"summary": {
"type": "string",
"description": "One-sentence description of the issue."
}
},
"required": ["priority", "summary"]
}
}
Handling Multiple Tools Without Confusion
When you register several tools in one request, overlapping descriptions are the most common cause of misrouting. Audit your tool set for ambiguity by asking: if I only read the names and descriptions, could two tools plausibly match the same user request? If yes, tighten the language or merge the tools.
Also watch parameter name collisions across tools. If get_user and update_user both take a field called id, make sure it means the same thing in both — Claude will reuse context across tool calls in a conversation, and inconsistent meaning for the same field name causes subtle bugs.
Validating and Handling Tool Results
Never trust tool_use arguments as pre-validated input. Run your normal schema validation (ajv, zod, pydantic, whatever your stack uses) before executing the underlying function. If validation fails, return the error as a tool_result with is_error: true rather than crashing — Claude can often self-correct and retry with fixed arguments on the next turn:
{
"type": "tool_result",
"tool_use_id": "toolu_01A2...",
"content": "Invalid order_id format. Expected ORD-YYYY-NNNNN.",
"is_error": true
}
This feedback loop is part of why function calling with Claude works well for multi-step workflows — the model can recover from malformed input instead of silently failing.
Testing Schemas Before Production
Before shipping a new tool, test it against edge-case phrasing: vague requests, requests that could match two tools, and requests missing information Claude would need to fill a required field. If Claude asks a clarifying question instead of guessing, that's working as intended — a well-designed schema with clear required fields should produce clarification requests rather than fabricated arguments.
If you're running function calling through an API layer rather than directly against Anthropic, make sure tool use, streaming, and the resulting metadata are all exposed consistently. SubToAPI turns your existing Claude access into a straightforward HTTPS API with full support for tool use, streaming, and the standard messages endpoint, so you can prototype schema changes without wiring separate infrastructure. The quickstart covers the request format in detail, and plans start on the pricing page with a free trial at signup.
Frequently Asked Questions
Should every parameter have a description, even obvious ones? Yes. Claude uses the description text to decide both which tool to call and how to fill each field. Even a field named email benefits from a one-line description specifying format expectations, like whether it must be lowercase or already verified.
How many tools can I register before accuracy drops? There's no hard limit, but routing accuracy degrades as tool count and overlap increase. Keep tool sets under roughly 15–20 for a single request, and split unrelated domains (e.g., billing vs. support) into separate conversations or tool subsets when possible.
Can I nest tool calls, where one tool's output feeds another tool's input? Yes — this is standard multi-turn tool use. You return a tool_result, Claude reads it in context, and it may issue another tool_use block referencing data from the previous result. Design each tool's output format with this chaining in mind, using consistent field names across tools.