What MCP tool annotations are
Tool annotations are optional fields in an MCP tool definition that describe how the tool behaves. They say whether it changes anything, whether changes are destructive, whether a repeat call is safe and whether it reaches outside systems. The server writes them, and clients must treat them as untrusted unless the server itself is trusted.
The schema calls every annotation field a hint, including the display title. An annotation tells you what the server claims about its tool, and nothing more.
The annotation fields and their defaults
The 2026-07-28 schema defines five annotation fields. Their defaults are cautious, so a tool with no annotations should be read as one that can change things, destructively, in the outside world. The example after the list shows them on a file-deletion tool.
- title: a display name for the tool. Even this is only a hint.
- readOnlyHint: true means the tool does not modify its environment. The default is false.
- destructiveHint: true means the tool may make destructive updates, and false means it only adds. The default is true, and the field only matters when readOnlyHint is false.
- idempotentHint: true means calling the tool again with the same arguments has no further effect. The default is false, and it also applies only when readOnlyHint is false.
- openWorldHint: true means the tool may interact with an open set of outside entities, as a web search does. False means a closed domain, such as a memory store. The default is true.
{
"name": "delete_file",
"description": "Delete a file inside the project directory",
"inputSchema": {
"type": "object",
"properties": { "path": { "type": "string" } },
"required": ["path"]
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": true,
"openWorldHint": false
}
}Why annotations cannot be trusted on their own
An annotation is a claim the server makes about itself. Nothing in the protocol checks it, and a malicious or buggy server can label a tool that deletes files as read-only. The schema itself says clients should never make tool use decisions based on annotations received from untrusted servers.
Descriptions share the weakness, and the model reads them as instructions. A server can also change a tool definition after you approved it, so annotations that were accurate at install time may not stay accurate.
Whether a server counts as trusted is your judgment, and its annotations cannot settle it. A server you build and run yourself, or one your organization has reviewed and pinned to a version, can reasonably be trusted. An unknown package from a public registry cannot.
Using annotations in approval flows
A safe rule: from an untrusted server, annotations may add friction but never remove it. In practice that looks like this:
- Require confirmation for every tool from an untrusted server, whatever its annotations say.
- Treat destructiveHint: true, or missing annotations, as a reason to show the full arguments, a preview or diff where possible, and a clear warning.
- Use readOnlyHint: true to skip prompts only for servers you trust, and keep a log of those calls with their arguments.
- Treat openWorldHint: true as a sign that data can leave the machine, which matters when the same agent can also read local files.
- Use idempotentHint from a trusted server to decide whether an automatic retry after a timeout is safe. It says nothing about whether the first call is safe.
- Record the tool definitions you approved, and ask again when a tool's annotations or description change.
Back the hint with real limits
Real enforcement comes from outside the server's claims: a read-only database role, a token with read scopes, a container with one directory mounted, or no network access. When a tool labeled read-only runs with read-only credentials, the label and the reality agree.
Accurate annotations from a trusted server are still useful. They let a client skip needless prompts for reads and focus attention on writes, which reduces the approval fatigue that makes people click yes without looking.