An unannotated tool is assumed destructive
When a model is handed a set of tools, it gets a name, a description and a schema for each. Nothing in that tells a client whether calling one reads a file or deletes a repository. So a client has two options, and both are bad: confirm every call, which makes the tool unusable, or confirm nothing, which makes it unsafe.
The Model Context Protocol has a small answer to this. A tool can carry annotations — four boolean hints that describe its behaviour rather than its interface.
- readOnlyHint
- The tool does not modify its environment. Defaults to
false. - destructiveHint
- The tool may perform destructive updates; if false, its updates are only additive. Meaningful only when the tool is not read-only. Defaults to
true. - idempotentHint
- Calling it repeatedly with the same arguments has no additional effect. Meaningful only when the tool is not read-only. Defaults to
false. - openWorldHint
- The tool interacts with an open world of external entities — a web search does, a memory store does not. Defaults to
true.
The defaults are the point
Read those defaults again. destructiveHint defaults to true. openWorldHint defaults to true. A server that ships no annotations is not making a neutral statement about its tools — it is asking every client to assume that each one is destructive and reaches out into the open world.
That is why this is a safety contribution and not a documentation chore. Annotating a read-only tool does not add a warning; it removes one that the specification had already applied on the tool’s behalf. An unannotated server makes a client choose between friction on everything and friction on nothing. An annotated one lets the client put the confirmation prompt where it belongs and leave the safe calls alone.
What annotations are not
Worth stating before the part where I claim credit for adding them, because the specification is blunt about it: every one of these properties is a hint, with no guarantee that it faithfully describes what the tool does. The schema instructs clients never to make tool-use decisions on annotations received from servers they do not trust.
So an annotation is a claim a server makes about itself, and a claim about yourself is not evidence. What it buys is narrower and more useful than trust: where trust already exists, annotations let a client be less pessimistic than the defaults require. Where it does not, they are inert — correctly. That is the same property the agent fleet is built on: a message may say where to look, and may never decide.
What I did
I read MCP servers maintained by other people, worked out which of their tools read and which of them write, and sent pull requests adding the annotations. Not my servers — theirs. Every change is a public pull request in somebody else’s repository, and each one had to persuade a maintainer who had no reason to accept it.
How it was received
A merged-only figure is a flattering one, so here is the denominator beside it: everything I opened, what was accepted, and what was turned down. The gap between the first and the other two is the batch still awaiting a decision.
The declines were mostly reasonable, and a few were interesting: maintainers who had deliberately chosen a different convention, or who wanted the hint set differently from the way I had read their code. Being told that a tool I had marked additive was in fact destructive is a better outcome than silence.
Two responses were worth more than the merge itself. On one large project the maintainer took the annotations and asked whether idempotentHint should be added too — the contribution turned into the maintainer extending it further than I had. On another, the maintainer held the pull request open until they had run manual checks against the change, and merged only once those passed. Both are the review working exactly as it should: the hint is a claim, and somebody who knows the code checked it.
Where they landed
Across 75+ repositories, including servers maintained by:
Names are on that list because the repositories are public and every pull request in them is public. Nothing here comes from anything I have reviewed privately, and nothing on this site ever will.
Why this is the page to check first
Most of what I build is private infrastructure, and a portfolio full of work nobody can inspect is a portfolio asking to be taken on faith. This campaign is the opposite. Every figure above links to the search that produces it, including the one counting the pull requests that were turned down, and the public record re-derives all of them in CI so a claim that stops being true fails the build.
It is also the smallest possible unit of the thing I actually do. The review pipelines exist because someone has to decide what is safe to ship, at a volume no one can read by hand. An annotation is that same decision, made once, by the person who wrote the tool, and carried in the protocol so that everyone downstream inherits it.