Tool schema design
The model decides whether and how to call a tool based entirely on its name, description, and parameter schema — nothing else. A vague or ambiguous schema doesn't fail loudly; it just quietly produces wrong or skipped tool calls, which is worse.
3 min read
The schema is the only information the model has
A tool's implementation code is invisible to the model. What it sees is exactly the name, description, and parameter schema exposed to it — the same three fields a human developer would need to correctly call a function they'd never seen the source of, with the added constraint that the model can't ask a follow-up question if something's ambiguous. Every real problem in tool schema design traces back to this: the schema has to carry all the information a correct call requires, because there's nothing else for the model to fall back on.
Naming: specific enough to disambiguate, not so long it's noise
Bad: "search" - search what? how?
Bad: "get_data" - which data?
Good: "search_orders_by_customer" - unambiguous about scope and target
Good: "get_available_appointment_slots"
When a tool catalog has more than a handful of tools, vague names create real collisions — a model deciding between search and lookup when both exist is guessing, not reasoning, because the names alone don't communicate a difference in scope. Specificity in the name reduces how much the description has to do the disambiguating work alone.
Descriptions: written for the model's decision, not for a human skimming docs
The description's job is answering "when should I call this, with what, and what do I get back" — not documenting the implementation.
// Weak — describes the mechanism, not the decision
description: "Queries the orders table"
// Strong — describes when to use it and what it returns
description:
"Look up a customer's past orders by customer ID. Returns order " +
"date, status, and total for each. Use this before answering any " +
"question about a specific customer's order history — do not " +
"guess at order details from conversation context alone."The second version does real work: it states the trigger condition ("before answering... order history questions"), and it pre-empts a specific failure mode (guessing instead of calling the tool) by naming it explicitly. Descriptions that read like internal code comments miss the actual job, which is steering a decision, not documenting a function.
Parameter schemas: types and required-ness are instructions, not just validation
Marking a field required, giving it a narrow type (an enum instead of a free-form string, when the valid values are known), and naming it precisely all reduce the space of wrong calls the model can make before your code ever runs. A status: string field the model might fill with "cancelled", "Cancelled", or "CANCELED" is a self-inflicted parsing problem; status: z.enum(["pending", "confirmed", "cancelled"]) makes the wrong values structurally unavailable to emit in the first place — the same schema-constrained-generation mechanism covered in the structured-outputs lesson, applied to tool arguments instead of a final response.
Error handling: tool errors are conversation turns, not exceptions to swallow
When a tool call fails — a lookup finds nothing, a downstream service times out — the error message that gets fed back into the conversation is itself an instruction to the model about what to do next. A bare "Error: 404" gives the model nothing to act on. A message like "No order found with ID 'ord_9928'. Ask the customer to confirm the order number, or use search_orders_by_customer to look it up instead" gives the model a concrete next step, and models genuinely follow this kind of guidance well when it's present. Treat the string returned on failure as part of the interface design, not an afterthought bolted onto a try/catch.
Further reading
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. A tool catalog includes both `search` and `lookup` tools with no further distinction in their names. What problem does this create?
2. What is a tool description's actual job, according to good schema design?
3. Why does constraining a `status` parameter to an enum (`"pending" | "confirmed" | "cancelled"`) instead of a free-form string reduce real bugs?
4. A tool call fails and the code returns "Error: 404" as the result fed back to the model. Why is this considered weak error-handling design?