Claude Tool Use Schema Design Tips That Work
If Claude is calling the wrong tool, passing malformed arguments, or skipping a tool call it should obviously make, the problem is almost never the model — it's the schema. Claude's tool use (function calling) relies entirely on the quality of your name, description, and input_schema fields to decide when and how to call a tool. A vague or overloaded schema produces vague or overloaded behavior.
This guide covers the practical schema design patterns that make tool calls reliable: how to structure descriptions, how to type parameters, how to handle optional fields, and the mistakes that cause silent failures in production.
Why Schema Quality Determines Tool Call Accuracy
Claude doesn't "know" your backend. It only sees the JSON schema you send in the tools array of your request. Every decision — which tool to call, what arguments to extract from the conversation, whether to call a tool at all — is based on pattern-matching against that schema and the surrounding context. That means schema design is prompt engineering, not just API plumbing.
A typical tool definition looks like this:
{
"name": "get_order_status",
"description": "Retrieve the current shipping status of a customer order by order ID.",
"input_schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The unique order identifier, e.g. ORD-48213"
}
},
"required": ["order_id"]
}
}
Every weak point in this structure — an ambiguous description, a missing example, a loose type — translates directly into a weaker decision by the model.
Naming Tools Clearly
Use a snake_case verb-noun pattern: get_weather, create_invoice, search_documents. Avoid generic names like handler or process_request — Claude has nothing to disambiguate against when multiple tools share vague names.
If you have several similar tools, make the distinction explicit in the name itself rather than relying on the description alone:
search_products_by_namesearch_products_by_categorysearch_products_by_price_range
This reduces the chance Claude picks the wrong one when the user's request is ambiguous.
Write Descriptions Like You're Onboarding a New Engineer
The description field is the single highest-leverage part of a tool schema. Treat it as documentation, not metadata:
- State exactly what the tool does and when it should be used.
- Mention what it does not do if there's a likely confusion with another tool.
- Include an example input/output pattern if the format is non-obvious.
- Keep it under a few sentences — long descriptions dilute signal.
Bad:
"description": "Gets data"
Good:
"description": "Looks up the current inventory count for a specific SKU in the warehouse system. Use this only for stock levels, not for pricing or product details."
The second version tells Claude both what to do and what not to do with a tool that might otherwise get confused with a pricing lookup.
Parameter Design: Types, Enums, and Constraints
Loose typing is one of the most common sources of bad tool calls. A few concrete rules:
- Use
enumwhenever the valid values are a fixed set. Don't let Claude guess a string like"status": "shipped"when it could just as easily produce"Shipped"or"in_transit". - Use specific types. Prefer
"type": "integer"over"type": "string"for numeric fields — this avoids downstream parsing errors. - Add
descriptionto every property, not just the top-level tool. A field namedscopeis meaningless without context;"description": "Either 'user' or 'organization'"resolves it immediately. - Mark fields
requiredexplicitly. Claude treats non-required fields as genuinely optional and may omit them, which is correct behavior — but only if your backend can handle the absence gracefully.
{
"type": "object",
"properties": {
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "urgent"],
"description": "Ticket priority level"
},
"assignee_id": {
"type": "integer",
"description": "Numeric ID of the support agent"
}
},
"required": ["priority"]
}
Keep Tool Boundaries Narrow
A tool that tries to do too much — manage_user handling creation, deletion, updates, and permission changes all through one giant optional-parameter schema — is harder for Claude to use correctly than four small tools: create_user, delete_user, update_user, set_user_permissions.
Narrow, single-purpose tools:
- Reduce the chance of Claude passing the wrong combination of parameters
- Make each tool's description easier to write precisely
- Make your backend logic simpler to implement and test
If you're worried about tool count bloating the context, remember that tool schemas are relatively cheap compared to verbose descriptions — it's usually better to have 10 clear, small tools than 3 overloaded ones.
Handling Nested Objects and Arrays
Claude can handle nested schemas, but nesting adds failure surface. If a parameter is an array of objects, document the shape explicitly:
{
"items": {
"type": "array",
"description": "List of line items, each with 'sku' (string) and 'quantity' (integer)",
"items": {
"type": "object",
"properties": {
"sku": { "type": "string" },
"quantity": { "type": "integer" }
},
"required": ["sku", "quantity"]
}
}
}
Don't rely on the description alone to convey structure when the full JSON Schema nesting can express it directly — the schema is more reliable than prose for structure, and prose is more reliable for intent.
Testing and Iterating on Schemas
Schema design is iterative. Log the actual tool_use blocks Claude returns in production and check for:
- Wrong tool selected for a given user message
- Missing required arguments that Claude should have extracted
- Hallucinated parameter values not present in the conversation
If you're testing this through an application API layer rather than hitting Anthropic directly, SubToAPI exposes the same tools schema format over a standard HTTPS endpoint with full request/response logging, which makes it easier to see exactly which schema version produced which tool call across a team. See the tool use docs for the request shape, or the quickstart if you're setting up API keys for the first time.
Common Mistakes to Avoid
- Overloading one tool with mutually exclusive parameters instead of splitting into separate tools.
- Omitting
required, causing Claude to leave out fields your backend assumes will always be present. - Using free-text strings for fixed categories instead of
enum. - Writing descriptions for humans skimming docs, not for a model matching intent — be literal and specific, not clever.
- Not versioning schemas when you change a tool's shape, which makes debugging historical tool calls painful.
FAQ
Does Claude ever ignore the schema and just guess arguments? No — Claude generates arguments that conform to your input_schema, but if the schema is vague or the field lacks a description, it will guess the value based on context, which is where most errors originate.
Should I use one large tool or several small tools? Prefer several small, single-purpose tools. They're easier to describe precisely and reduce the chance of Claude mixing up parameters meant for different operations.
How do I force Claude to always call a specific tool? Use the tool_choice parameter to require a specific tool or any tool call, rather than relying on description wording alone — check the messages docs for the exact request format.