When an AI agent calls the wrong tool or passes the wrong argument, the instinct is to blame the model. Often the real problem is the tool: a vague name, an ambiguous parameter, an error message written for humans, or an output too large to read. Tools are the interface between the model and your systems, and like any interface, their design determines how easily they are used correctly.
This guide covers how to design tools, also called functions, for LLM agents: naming, descriptions, parameters, outputs, errors, granularity and permissions. It applies whether you define tools directly in an API, through the Model Context Protocol (MCP), or in an agent framework.
Think of the model as a new developer reading your docs
A model sees your tools only through their names, descriptions and schemas. It has no access to your codebase, your wiki or the conversation where someone explained what "sync" means. Every tool definition should be understandable by a capable developer who has never seen your system, with nothing else to go on.
Names: specific verbs and nouns
- Use verb-noun names that describe the effect:
search_orders,get_order,refund_order,send_customer_email. - Avoid vague verbs like
process,handle,manageorsync. - Distinguish read from write:
get_andsearch_for reading, explicit verbs for actions with side effects. - Namespace when you have many tools:
billing_refund_order,crm_update_contact.
Descriptions: when to use it, and when not to
A good description answers four questions: what the tool does, when to use it, when not to use it (pointing to the right alternative), and any important side effects or limits.
{
"name": "search_orders",
"description": "Search a customer's orders by date range or status. Use this when the user does not know the order number. If you already have an order number, use get_order instead. Returns at most 20 orders, newest first.",
"input_schema": {
"type": "object",
"properties": {
"customer_id": {"type": "string", "description": "Internal customer ID from the authenticated session, e.g. 'cus_8f2a'. Never ask the user for it."},
"status": {"type": "string", "enum": ["pending", "shipped", "delivered", "cancelled"]},
"from_date": {"type": "string", "description": "Inclusive start date, YYYY-MM-DD, in the customer's time zone."},
"to_date": {"type": "string", "description": "Inclusive end date, YYYY-MM-DD."}
},
"required": ["customer_id"]
}
}Parameters: remove ambiguity
Most wrong-argument failures come from ambiguous parameters.
- Name parameters precisely:
check_in_daterather thandate;amount_centsrather thanamount. - State formats and units in descriptions: date formats, time zones, currencies, units of measure.
- Use enums for fixed choices so the model cannot invent values.
- Include an example value in each description.
- Keep required parameters minimal and give sensible defaults to the rest.
- Avoid parameters the model cannot know. Identity and permissions should come from the session, not from model-supplied arguments.
Outputs: return what the model needs, nothing more
Tool outputs enter the model's context and cost tokens on every subsequent step.
- Return relevant fields only, not entire database records with internal IDs and metadata.
- Use readable identifiers alongside opaque ones (a name and an ID, not just an ID).
- Paginate or truncate large results, and say so explicitly: "Showing 20 of 312 results; refine by date to narrow."
- Return structured data consistently, so the model can rely on the shape.
- Confirm the effect of actions: "Refund of €42.00 issued for order A-1029, reference R-5521." This gives the agent evidence to report and helps prevent false success claims.
Errors: tell the model what to do next
Error messages written for humans ("Something went wrong") leave agents guessing, retrying blindly or reporting success. Write errors for the model:
| Bad | Good |
|---|---|
| Error 400 | Invalid from_date "11/10/2026": use YYYY-MM-DD, e.g. 2026-11-10. |
| Not found | No order A-1092 for this customer. Use search_orders to list recent orders. |
| Permission denied | Refunds over €500 require human approval. Use request_approval with the order ID and reason. |
| Timeout | Payment service timed out; no refund was issued. Safe to retry once after 5 seconds. |
Good errors state what was wrong, whether any side effect happened, and what to do next. They prevent loops and false success reports, two of the most common AI agent failure modes.
Granularity: not too many, not too broad
- Too many fine-grained tools (one per database column) overwhelm the model and lengthen plans.
- Too few broad tools (
execute_sql,call_api) give the model enormous power and room for error. - Aim for tools that match user-level tasks:
reschedule_appointmentrather than separateget_slot,lock_slot,update_recordandnotifycalls, when that sequence is always the same.
When a sequence of calls is always identical, combine it into one tool. Deterministic sequences belong in code, not in the model's plan.
Permissions and safety
- Least privilege: give each agent only the tools it needs, with the narrowest scope possible.
- Enforce authorisation in the tool, using the authenticated user's identity, on every call. Never trust a model-supplied user ID.
- Separate read and write tools, so read-only agents cannot take actions.
- Require confirmation for irreversible actions: a two-step
prepare_refund/confirm_refunddesign or a human approval tool; see human-in-the-loop patterns. - Treat tool outputs as untrusted data when they include user-generated or external content, which may contain prompt injection; see how to red-team an LLM chatbot.
- Rate-limit and budget expensive tools; see step budgets and stopping rules.
Versioning and changing tools
Tools change as your systems change, and every change can alter agent behaviour. Treat tool definitions as a public API:
- Version them alongside prompts, and record which version each agent run used.
- Add rather than rename. Renaming a parameter or changing its format silently breaks the patterns the model relied on. Add a new parameter, migrate, then remove the old one.
- Re-run your agent evals after any change to names, descriptions, schemas or outputs, not only after code changes.
- Deprecate explicitly. If a tool is being replaced, say so in its description and point to the new tool.
Testing tools with the model
Test tool definitions the way you test prompts. Write tasks that should use each tool, tasks that should use a different tool, and tasks that should use no tool. Run them several times and check which tools were called with which arguments, and whether the end state is correct. Watch for confusion between similar tools; it usually means their descriptions overlap. Our guide tool-use evals are broken explains how to grade outcomes rather than exact calls.
A tool design checklist
- Verb-noun name that describes the effect.
- Description covering what, when, when not, and side effects.
- Precisely named parameters with formats, units, enums and examples.
- Identity and permissions from the session, not arguments.
- Compact, structured outputs with readable identifiers.
- Errors that say what went wrong, what happened, and what to do next.
- Granularity matching user-level tasks.
- Confirmation or approval for irreversible actions.
- Tests for correct use, wrong-tool confusion and no-tool cases.
Frequently asked questions
What makes a good tool description for an LLM?
It says what the tool does, when to use it, when to use a different tool instead, and any side effects or limits, with parameter descriptions that specify formats, units and example values.
How many tools should an agent have?
As few as the task needs. Large toolsets increase confusion and planning errors. Group related tools, combine fixed sequences into single tools, and give each agent only the tools for its role.
How should tools report errors to an agent?
In plain, specific language that states what was wrong, whether any side effect occurred, and what the agent should do next, such as correcting a format, using another tool or requesting approval.
Should an agent be able to choose which user it acts for?
No. Identity and permissions should come from the authenticated session and be enforced inside each tool. Never accept a user or account ID from the model as proof of authorisation.



