Your tool description is a protocol, not documentation
When a model decides whether to call what you shipped, everything it has is the name, the description and the schema.
The README, the docs site, the comment block — none of that is in its context. That makes the description part of the
protocol, and it changes what belongs in it.
The expensive failures are the silent ones
A tool that crashes is cheap: you get an error, an exit code, a line number. The expensive failures are the ones
where the model made a reasonable decision from the information it had:
- it never called the tool, because nothing in the description said when to call it;
- it called it on input the tool cannot serve, because nothing said what the tool is not for;
- it called it with three options packed into one parameter, because the schema said
stringand nothing else; - it re-called it after a timeout, because the description never mentioned that a retry is a new paid call.
None of these raise an exception. They produce a plausible answer from the wrong tool, and you find out weeks later
by accident. The fix is almost never in the code — it is in the four fields the model can actually see.
What goes in, by field
| Field | The question it answers for the model | What belongs there | What usually sits there instead |
|---|---|---|---|
| name | What is this, in one token? | Verb + object (decide, get_result) |
An internal codename |
| description opening | When should I reach for this? | The trigger condition, first sentence, no preamble | "This tool allows you to…" |
| description negative | When should I not? | The nearest lookalike tasks you do not serve | Nothing |
| description mechanics | What happens after I call? | Latency, cost, retry rule, where the result appears | A feature list |
| parameter descriptions | What shape is valid here? | Units, required-ness, one value per field | The parameter name, restated |
| annotations | Can I call this freely? |
readOnlyHint, idempotentHint, openWorldHint — honestly |
Omitted, or "true" by wishful default |
| output schema | What comes back, in what shape? | Field names, types, and which ones are always present | Absent, so the model guesses |
Every row is a question the model cannot answer any other way. If a row is empty, you have not made a choice — you
have delegated it to sampling.
Two of those rows are the ones people skip
The negative case. A trigger condition with no boundary is an invitation to over-call. Saying what the tool is
not for costs one sentence and removes an entire class of misuse. Here is the boundary as we publish it for our
own endpoint, verbatim from the live tools/list:
Not for: more than two options; anything an objective rule settles (a spec, a test, a price, a document); factual
lookup; paths that must answer in seconds; high-stakes irreversible calls without review.
That list does real work: four of the five items are tasks a model would otherwise bring to a preference engine,
because from the outside they look like judgement calls.
The retry rule. If a call can time out, the description must say what to do when it does — otherwise the model's
default (try again) is the one behaviour you cannot afford. Ours states the cost and the alternative in the same
breath: a retry is a new paid call; retrieve it instead with get_result, which is read-only and free. Whether
your tool is paid or not, the shape of the sentence is the same: after a cut-off, what should the model do?
The artifact: seven questions before you ship a tool
Run this against each tool in your server. Every "no" is a decision you have not made yet.
- Trigger — does the first sentence say when to call it, naming the task rather than the feature?
- Boundary — does it say what it is not for, including the nearest lookalike task?
- Inputs — can each parameter be filled correctly without reading any other document? Units, formats, one value per field, no bundling.
- Return — does the output schema name what comes back, including which fields are always present and which are conditional?
- Latency and cost — does the description state how long the call may take, and whether calling it again costs something?
- Recovery — after a cut-off or a failed call, is there a stated path that is not "call it again"? If the answer is another tool, that tool needs to be named here.
-
Annotations — are the hints you declare true?
idempotentHint: trueon a call that bills or writes is a lie the client will act on.
You can read the contract the model actually sees, over the same credential-free discovery call its host makes:
curl -s -X POST https://mcp.turingcorp.net/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Point that call at your own server and read the reply as a spec review: name, description, parameters, annotations
and output schema — nothing else travels with the call.
Two properties make this list worth keeping. First, it is checkable without a model in the loop: 1–3 and 5–7 are
literally "is this sentence present in the description". Second, it is testable when it fails: each question maps to
a wrong-call shape you can reproduce, so a description change is a change you can regression-test like any other
contract.
Why we publish the description instead of a prompt trick
The same asymmetry shows up on our side. Our endpoint is a remote MCP server that answers one narrow question — you
give it two defensible options and it returns which it prefers, how far apart it judged them, and why — and the
entire contract for it lives where the model reads it: the tool description, the parameter descriptions and the
output schema, all readable with no credential at https://mcp.turingcorp.net, via tools/list and /llms.txt.
Ranges of accuracy, sample sizes and method are published separately at https://api.turingcorp.net, because a
number the model reports about itself is worth nothing unless something outside the model has checked it.
A description that answers those seven questions makes the tool callable. A description that answers none of them
makes it a coin flip with a schema.
Top comments (0)