Blog / How to optimise your MCP tools by knowing how agents call them
Your “read-only” hints can make agents spend money: getting MCP tool annotations right
Annotations look like documentation. To some agent apps they're policy: they decide whether a call needs the user's approval. Get them wrong and the app waves through the calls you most wanted it to stop.
What the hints mean
| Hint | Means | Set it when |
|---|---|---|
readOnlyHint: true | Doesn't change anything | Pure reads inside your system |
destructiveHint: true | May cause changes the user should approve | Spends, starts, sends, deletes, overwrites |
idempotentHint: true | Calling twice is harmless, safe to retry | Same call, same effect, no extra cost |
openWorldHint: true | Reaches outside systems | Web fetches, third-party APIs, messages |
What we found
We read the annotations of a 99-tool production MCP server (43 marked read-only, 44 marked destructive) and compared them with the tools' own descriptions.
- A tool whose description says “it spends a little”:
destructiveHint: false, idempotentHint: true. - A tool whose description says “it spends credits”: the same.
- A tool that starts searches:
destructiveHint: true, butidempotentHint: true, which invites retries. - A public-page reader:
readOnlyHint: true, openWorldHint: true. Accurate, but in a “just look” test job an agent tried it 3 times; our allowlist blocked it. See test agents against production read-only.
Check your annotations against what tools really do.
We compare every tool's hints with its behaviour and test how the agent apps your users run treat them.
How apps use them
In a live check, Codex exec with approval policy “never” refused every tool on that server marked destructive, unless the tool was approved by name. That's a real guard, and it keys off your hint. A spend tool marked non-destructive skips it.
Other apps differ and change between versions; some ask before any MCP call, some auto-approve by setting. Don't rely on any one behaviour: make the hints true.
How to annotate
// spends credits or starts work
"annotations": {"readOnlyHint": false, "destructiveHint": true,
"idempotentHint": false, "openWorldHint": true}
// pure read inside your system
"annotations": {"readOnlyHint": true, "openWorldHint": false}
- Small spends count. “It spends a little” is still a spend.
- Starting a job isn't idempotent: twice means two jobs.
- Say the cost in the title or first line too, so models see it: see put the meaning in the first line.
- Pair spend tools with a preview-then-confirm flow: the two-step confirm pattern.
Go deeper: Agent refusal states, How to optimise your MCP tools, MCP scorecard.
How to check yours
- List every tool with its four hints.
- Grep descriptions for spend, credit, charge, send, start, launch, delete.
- Flag any match that's non-destructive or idempotent.
- Run the MCP scorecard: it flags write-like names marked safe.
Back to how to optimise your MCP tools.
How we measured
- Annotations: the live
tools/listof a 99-tool production server, 16 Oct 2026, compared with each description. - Codex behaviour: a live write-shape probe of 7 non-mutating calls (Codex CLI 0.161.0), with the record re-read before and after: unchanged.
- Blocked page reads: the 24-run skills trial through a read-only allowlist proxy.
- Limits: one server; app behaviour checked in Codex only.
FAQ
How do MCP tool annotations affect agents?
They're hints the agent app may use to decide what needs the user's approval or what's safe to retry. In our tests Codex exec refused tools marked destructiveHint: true unless they were approved by name, so the hint changed behaviour.
Should a tool that spends credits be destructive?
Yes. Mark anything that spends money or credits, starts work, sends messages or writes data as destructiveHint: true and idempotentHint: false, even if the amount is small.
What does idempotentHint mean?
That calling the tool twice with the same arguments has no extra effect, so retrying is safe. A tool that starts a search or charges per call isn't idempotent.
Is readOnlyHint enough to keep testing safe?
No. It's a hint, it can be wrong, and read-only tools can still reach the outside web. For safe testing, enforce an allowlist outside the agent.
Check your annotations against what tools really do.
We compare every tool's hints with its behaviour and test how the agent apps your users run treat them.