What are tool annotations, and why are they hints, not guarantees?
Quick Answer
Tool annotations are optional metadata a server can attach to a tool definition to describe its behavior to the host: readOnlyHint (doesn't modify state), destructiveHint (may perform irreversible changes), idempotentHint (calling it repeatedly with the same arguments has no additional effect), and openWorldHint (interacts with external/unbounded systems, e.g. the open internet, versus a closed local system). Hosts use these to drive UX — e.g., requiring extra confirmation before calling a tool annotated destructiveHint: true. They're explicitly hints, not guarantees, because they're self-reported by the server. A malicious or buggy server can annotate a destructive tool as readOnlyHint: true, so a host must not treat annotations as a security boundary, only as a UX signal for well-behaved, trusted servers.
Detailed Answer
Annotations give a host enough signal to build sensible default UX around risky actions. But they only work if you remember who's making the claim.
The four standard annotations
{
"name": "delete_file",
"description": "Permanently delete a file from disk.",
"inputSchema": { "type": "object", "properties": { "path": {"type": "string"} }, "required": ["path"] },
"annotations": {
"title": "Delete File",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": true,
"openWorldHint": false
}
}
| Annotation | Meaning |
|---|---|
readOnlyHint | Tool does not modify its environment |
destructiveHint | Tool may perform irreversible or destructive updates (only meaningful when readOnlyHint is false) |
idempotentHint | Calling it again with identical arguments has no additional effect beyond the first call |
openWorldHint | Tool interacts with an open-ended external domain (e.g., web search) rather than a fixed, closed set of entities |
How a host is expected to use them
A reasonable host policy: auto-approve calls to tools annotated readOnlyHint: true without prompting. Require explicit user confirmation for anything with destructiveHint: true. Treat idempotentHint: false tools as unsafe to silently retry after a timeout, since a retry could duplicate a real-world effect like sending an email twice.
Why "hint," not "contract"
The annotation is written by the server author. The server is exactly the thing a host may not fully trust — it could be a third-party plugin the user just installed. A server has no obligation to be honest. It could label delete_all_data as readOnlyHint: true to slip past a host's confirmation UI. This is precisely why the spec calls them hints. Treating them as an enforceable security boundary would create a false sense of safety. A host that actually cares about safety still needs independent trust signals: provenance of the server, sandboxing, and user awareness of what a newly installed server can do. Annotations only optimize the common case where the server behaves honestly.