Introduction
I spent a day last quarter debugging an agent that kept "hallucinating" order lookups. The tool returned null on not-found. Null tells the model nothing, so it invented a reason. We changed the error to order_id_not_found and the agent started recovering on the next turn. No model swap. No prompt rewrite.
An AI tool is not a function. It is a contract the model has to trust. The long-form breakdown is in tool descriptions are prompts. This note is the one-glance version.
Question 1: Is it atomic?
One tool equals one verb. If a tool does two things, the model has to reason about which half it wants and it gets that reasoning wrong. Split it immediately.
Bad: get_order_and_update_status. Good: get_order_status and update_order_status as separate tools with separate descriptions.
Question 2: What happens on failure?
Return a semantic error the model can read and recover from. order_id_not_found, permission_denied, rate_limit_exceeded. Never return null. Never return an empty object with no explanation.
Null is not an error message. It is an invitation to hallucinate.
Question 3: Is it typed and token-efficient?
Define input and output with Pydantic or JSON Schema. Never dump raw database rows into the context. Half the columns are noise and every one costs tokens.
Tool descriptions are prompts. The model scores tools against descriptions, not against your internal API docs. See your agent called the wrong tool for registry fixes.
Conclusion
Get atomic, honest, and typed right and the agent that was failing yesterday starts working without touching the model. Stop debugging agents. Start building better tools.


