Writing Tool Descriptions the Model Will Actually Use Correctly
Models pick tools from names, descriptions, and parameter docs, not from your internal wiki.
Search across all documentation pages
Models pick tools from names, descriptions, and parameter docs, not from your internal wiki.
Vague or overlapping text produces wrong tools, skipped tools, or invented arguments. This page is a recipe for wording that steers selection without turning every schema into a novel.
Write each tool as a mini-spec: what it does, when to use it, when not to use it, and what each parameter means with units and examples. Keep descriptions non-overlapping across the tool list, and verify selection with golden prompts.
get_order, create_refund), not a vague noun (data, helper).TOOLS = [
{
"type": "function",
"function": {
"name": "get_order_status",
"description": (
"Fetch live fulfillment status for one order by numeric id. "
"Use when the user asks where an order is, ETA, or shipped/delivered state. "
"Not for placing orders, cancellations, or multi-order reports."
),
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "integer",
"description": "Numeric order id, e.g. 1042. Digits only; no # prefix.",
}
},
"required": ["order_id"],
"additionalProperties": False,
},
},
},
{
"type": "function",
"function": {
"name": "search_orders_by_email",
"description": (
"List recent orders for a customer email when the order id is unknown. "
"Use to discover order ids. For status of a known id, call get_order_status instead."
),
"parameters": {
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "Customer email, e.g. ada@example.com",
},
"limit": {
"type": "integer",
"description": "Max rows to return (1-20). Default 5.",
"minimum": 1,
"maximum": 20,
},
},
"required": ["email"],
"additionalProperties": False,
},
},
},
]The pair above encodes a clear handoff: search when id is missing, status when id is known.
On each turn the model scores candidate actions against the user goal and the tool list in context. Descriptions that share adjectives ("gets data", "manages orders") collapse into noise. Specific triggers ("when the user lacks an order id") create separable decision boundaries.
| Pattern | Example | Effect |
|---|---|---|
| verb_noun | cancel_order | Clear side effect |
| vendor jargon | oms_v2_mut | Model may avoid or misuse |
| overloaded | handle | Attracts every task |
| symmetric pair | get_* / list_* | Helps read vs search |
Prefer stable names. Renaming mid-flight breaks prompts, evals, and MCP clients that cached the old name.
You rarely need more than 2-4 sentences. If policy is long, put shared rules in the system prompt and keep tool text local.
cents, ISO-8601 UTC, IANA timezone).E.164 phone, ISO country code).enum when the set is small and closed.value that might be id or name).Symptoms:
Fixes:
| Put in system prompt | Put in tool description |
|---|---|
| Global safety and tone | When to pick this tool |
| Max refund policy overview | Parameter constraints for this call |
| "Prefer tools over memory for live data" | What this tool is not for |
Duplicating a long policy in every tool wastes tokens and drifts.
CASES = [
{"prompt": "Where is order 1042?", "expect_tool": "get_order_status"},
{"prompt": "What orders does ada@example.com have?", "expect_tool": "search_orders_by_email"},
{"prompt": "Write a haiku about shipping", "expect_tool": None},
]
# Run each through your agent with stubbed tools; assert first tool name.Treat description edits like code: small change, re-run the suite.
tenant_id, require it or inject it host-side; do not hope the model invents it.enum, not only in English.| Approach | When it wins | When it loses |
|---|---|---|
| Careful native descriptions (this recipe) | Small-medium tool sets | Extremely large catalogs |
| Tool search / deferred loading | Huge libraries | Extra hop latency |
| Router agent picking a specialist | Distinct domains | Overhead for simple apps |
Single mega-tool with a action enum | Tiny prototypes | Becomes an untyped RPC mess |
| Free-form text tools | Legacy models without function calling | Brittle parsing |
Usually 1-4 sentences. If you need a page, the tool is probably too broad or policy belongs in the system prompt.
Prefer meaning, format, and examples. The schema already carries type; English should add what the type cannot.
Short examples help format. Avoid implying the only valid business case is the example case.
Stronger models tolerate mess better, but clear boundaries help every model and reduce cost from repair turns.
Only high-level ("returns not_found when id is unknown"). Detailed error shaping belongs in tool results; see the errors page in this section.
Plain sentences travel best across providers. Fancy markdown is not guaranteed to help selection.
Map user language in the description ("invoice number is order_id") so the model can translate.
On every tool add/rename and when production shows rising wrong-tool rates.
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.
Reviewed by Chris St. John·Last updated Jul 16, 2026