Every tool exposed to a language model comes with a name, a description and a schema. Developers habitually treat these as documentation — something to write once and tidy later. They are not. They are the only thing the model has when it decides which tool to reach for, and the only thing it has when interpreting what came back.
Wrong description, wrong tool
A model choosing between fifteen tools reads the descriptions and picks. If two descriptions are similar, it will pick inconsistently. If one is vague, it will either never be called or be called for things it cannot do, and the user sees an unhelpful answer with no indication of why.
The fix is boring and effective: say what the tool returns, what it needs, and what it is not for. The last part is the one people skip and the one that prevents the most misuse.
Annotations change behaviour
Modern tool protocols carry hints alongside the description — whether a tool only reads, whether calling it twice is safe, whether it touches the outside world. Clients use these to decide what to run without asking and what to confirm first.
Get them wrong in the safe direction and you annoy people with confirmations. Get them wrong in the unsafe direction and a client will silently execute something that changes state. We shipped exactly that inversion once: two tools that create and cancel things were marked read-only, because the annotation was applied to the whole set instead of per tool. Everything passed. Nothing was read-only about them.
The schema is a contract with a non-negotiator
A human who sends a malformed request reads the error and fixes it. A model does the same, but each retry costs a round trip and some of them give up. Tight schemas with enumerated values and sensible defaults make the first attempt succeed, which is worth more than it sounds.