Writing tool descriptions and schemas that agents use reliably
A practical craft guide to naming tools, writing descriptions, and designing JSON schemas so language models call the right function with the right arguments.
When an agent fails in production, the instinct is to blame the model. Sometimes that is fair. Often the real problem is quieter: the tools themselves are hard to use. The model sees a list of names, short descriptions, and JSON Schema parameter lists, and it has to decide which tool to call and how to fill every argument. If those definitions are vague, overlapping, or full of optional traps, even a strong model will guess wrong. This article is about the craft of writing tools that agents can actually use, drawing on the official guidance from OpenAI, Anthropic, and the Model Context Protocol.
Why the tool definition is a prompt
Tool calling is not a separate magic channel. Providers inject your tool definitions into the model's context, usually as part of a constructed system prompt, and you pay for them as input tokens on every turn. OpenAI's function calling guide is explicit that callable function definitions count against the context limit and are billed as input. Anthropic's tool use overview says the same thing and also notes that enabling tools adds a special system prompt whose size depends on the model and on how you set tool choice.
That means a tool definition is prompt engineering. Anthropic makes the point twice, once in the product docs and once in their engineering essay Building effective agents: invest as much care in the agent-computer interface as you would in a human-computer interface. Their appendix on prompt engineering tools argues that you should put yourself in the model's shoes, write definitions the way you would write a great docstring for a junior teammate, and change argument shapes so mistakes are harder to make. The craft is not about sounding clever. It is about making the correct call the obvious call.
Names that draw clean boundaries
Start with the name. OpenAI recommends clear, detailed function names and says functions should be predictable and intuitive, following the principle of least surprise. Anthropic's implement tool use page says tool names must match ^[a-zA-Z0-9_-]{1,128}$, and it recommends meaningful namespacing when tools span services: github_list_prs and slack_send_message are easier to tell apart than two tools both named list or send. Under the MCP tools specification, each tool is uniquely identified by a name and carries a human-readable description plus an inputSchema written in JSON Schema. Across all three ecosystems, the name is the first filter the model uses.
A useful test is whether two tools could be confused by someone who only read their names. If you have search_docs and search_tickets, the difference is clear. If you have search and find, it is not. When tools are similar, Anthropic suggests consolidating related operations into fewer tools with an action parameter rather than creating a separate tool for every tiny verb. Fewer, more capable tools reduce selection ambiguity. OpenAI's soft guidance is complementary: aim for fewer than about twenty functions available at the start of a turn, and defer large or rarely used surfaces with tool search when your stack supports it.
Descriptions that answer the intern's questions
The description is where most of the craft lives. Anthropic calls extremely detailed descriptions the most important factor in tool performance. A good description should say what the tool does, when to use it and when not to, what each parameter means, and any important caveats about what the tool does not return. They recommend aiming for at least three or four sentences for each tool, more if the tool is complex. Their own contrast between a good and a poor get_stock_price description is worth studying: the good one explains that the ticker must be a valid US exchange symbol, that the return value is the latest trade price in USD, when to use the tool, and that it will not provide other company information. The poor one says only "Gets the stock price for a ticker."
OpenAI's best practices line up closely. Explicitly describe the purpose of the function and each parameter, including format, and what the output represents. Use the system prompt to say when and when not to use each function. Include examples and edge cases when you are fixing recurring failures, with one caveat: OpenAI notes that adding examples may hurt performance for reasoning models, so treat few-shot snippets as something you measure rather than something you always add. Anthropic offers a different path for examples: an optional input_examples field on the tool definition, validated against the schema, which is especially useful for nested objects and format-sensitive parameters. Those examples cost tokens, often roughly twenty to fifty for simple cases and more for nested ones, so use them where the schema alone is not enough.
A practical habit is to draft the description as if you were answering the questions a new hire would ask after reading only the name. What does success look like? What does failure look like? What IDs are accepted? Are dates ISO-8601? Does the tool mutate state? If the model keeps calling the wrong tool, strengthen the when-not-to-use language in both tools rather than burying a rule only in the system prompt.
Schemas that make invalid states hard
Parameters are defined with JSON Schema. The JSON Schema guide is the shared language underneath OpenAI function tools, Anthropic input_schema objects, and MCP inputSchema fields. Use it to remove ambiguity the description cannot carry alone. Enums prevent invented values. Required fields stop the model from omitting arguments you cannot default safely. Object shapes beat pairs of booleans that can contradict each other. OpenAI's example of a bad design, toggle_light(on: bool, off: bool), is exactly the kind of invalid state a schema should make impossible.
On OpenAI, setting strict: true makes function calls adhere to the schema instead of being best effort. Strict mode requires additionalProperties: false on each object and requires every property listed in properties to appear in required. Optional fields are expressed by allowing null in the type, not by leaving them out of required. Anthropic exposes a similar strict flag on custom tools so arguments match the schema exactly. Both providers are telling you the same thing: do not rely on hope when the API can enforce structure.
Also omit arguments the application already knows. OpenAI's guidance is to avoid making the model fill fields you already have from context, such as an order_id from a previous step. Combine functions that are always called in sequence. Keep the format close to what models see in ordinary text, and avoid formats with high overhead such as fragile diffs or heavily escaped JSON-inside-JSON, which is the ACI advice Anthropic developed while building their SWE-bench agent.
Here is a compact OpenAI-style definition that follows those rules. It names the tool clearly, describes when to use it, constrains units with an enum, enables strict mode, and marks the optional unit field with a nullable type:
{
"type": "function",
"name": "get_weather",
"description": "Retrieves current weather for a city and country. Use for live conditions only, not forecasts or historical averages. Returns temperature and a short conditions string.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country, e.g. Bogotá, Colombia"
},
"units": {
"type": ["string", "null"],
"enum": ["celsius", "fahrenheit"],
"description": "Temperature units. Pass null to accept the tool default of celsius."
}
},
"required": ["location", "units"],
"additionalProperties": false
},
"strict": true
}
The Anthropic and MCP shapes are nearly the same idea with different field names: Anthropic uses input_schema, MCP uses inputSchema, and MCP may also declare an outputSchema so clients can validate structured results. If you author tools once as JSON Schema and need provider-specific wrappers, our free JSON Schema to Tool Definition converter shows how the same schema looks in OpenAI, Anthropic, and MCP formats.
What you return matters as much as what you ask for
A reliable tool is not finished when the model can call it. The result you send back becomes part of the history the model re-reads on later turns. Anthropic recommends returning only high-signal information: semantic identifiers such as slugs or UUIDs, and only the fields needed for the next decision. Bloated responses waste context and make extraction harder. MCP distinguishes protocol errors (unknown tool, invalid arguments) from tool execution errors returned with isError: true, which lets the model see a failed business operation without treating it as a broken session. Design your error strings the same way you design success payloads: short, specific, and actionable, so the agent can retry or ask the user a useful question.
This is also where cost craft and description craft meet. As we covered in Why your agent costs more than you think, tool definitions and tool results are re-sent across the loop. A crisp schema and a trimmed result shrink both the chance of a bad call and the bill for a good one.
A short rewrite loop you can run this week
Pick one agent that mis-calls tools today. For each tool it uses, rewrite the name until a colleague can tell it apart from its neighbors, expand the description until the intern test passes, and tighten the schema until invalid calls are structurally hard. Turn on strict mode where your provider supports it. Run a handful of real prompts, including the ones that used to fail, and read the raw tool calls rather than only the final answer. Where the model still drifts, adjust the description or the argument shape before you reach for a larger model. Anthropic's agent essay puts it cleanly: carefully craft the agent-computer interface through thorough tool documentation and testing. The model is choosing from the menu you printed. Make the menu unmistakable.
Sources
OpenAI, Function calling (best practices, strict mode, token usage): https://developers.openai.com/api/docs/guides/function-calling. Anthropic, Tool use overview: https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview. Anthropic, Implement tool use (definitions, descriptions, input_examples): https://platform.claude.com/docs/en/agents-and-tools/tool-use/implement-tool-use. Anthropic, Building effective agents (Appendix 2, prompt engineering your tools): https://www.anthropic.com/engineering/building-effective-agents. MCP specification, Tools (2025-06-18): https://modelcontextprotocol.io/specification/2025-06-18/server/tools. JSON Schema, Understanding JSON Schema: https://json-schema.org/understanding-json-schema/. Checked October 16, 2026.