Imagine Claude is about to call a tool named delete_records. Should it ask for confirmation first? Without any metadata about what the tool actually does, a client has two bad options: always ask (interrupting everything, including harmless read operations) or never ask (silently deleting production data). MCP tool annotations solve this by letting server builders declare the behavioral characteristics of each tool — so clients can make informed decisions about when to pause and when to proceed.
What Tool Annotations Are
Tool annotations are an optional annotations field you can add to any tool definition in your MCP server. They are plain JSON booleans that describe the tool's behavior — not what it does, but how it behaves with respect to state and safety.
They were introduced in the MCP spec version 2025-03-26 (the March 2025 update). If you built your server against the original November 2024 spec, you'll need to upgrade to use them. Read the full spec comparison between MCP 2024 and March 2025 to understand all the changes in that update.
Annotations live directly inside the tool definition object, alongside name, description, and inputSchema. They are advisory — the MCP spec explicitly says they are hints for client UX decisions, not protocol-level enforcement.
The Three Annotations
1. readOnlyHint
Type: boolean
Meaning: When true, this tool never modifies any state. It only reads and returns data.
A tool with readOnlyHint: true is safe to call without asking the user first. It cannot delete anything, change any settings, write to any database, or send any messages. The worst thing that can happen is it returns data the user didn't want to see.
Example tools that should have this annotation:
read_file— reads a file, returns contentlist_directory— lists files in a folderget_user— retrieves a user record from a databasesearch_database— queries a database and returns resultsget_weather— fetches weather data from an API
2. destructiveHint
Type: boolean
Meaning: When true, this tool makes changes that cannot be undone. Data is permanently deleted, overwritten, or sent in a way that cannot be recalled.
Clients that see destructiveHint: true should display an explicit confirmation dialog before calling the tool. Claude Desktop already does this. Future clients may add additional affordances: a red warning color, a mandatory typed confirmation, or a summary of what will be affected.
Example tools that should have this annotation:
delete_file— permanently removes a filedrop_table— drops a database table and all its datapurge_cache— irreversibly clears a cachesend_email— sends an email that cannot be unsentpost_to_social_media— publishes publicly
3. idempotentHint
Type: boolean
Meaning: When true, calling this tool multiple times with the same inputs produces the same result. It is safe to retry.
Idempotency is a concept from HTTP and distributed systems: a PUT request is idempotent (setting a value to X twice results in X), while a POST request often isn't (posting the same message twice creates two posts). The same distinction applies to MCP tools.
This annotation matters for reliability: if a tool call times out or the connection drops, a client that knows the tool is idempotent can safely retry automatically without worrying about double-effects.
Example tools that should have this annotation:
set_config_value— setting a key to "dark mode" twice still results in dark modecreate_or_update_user— upsert operations are idempotent by designenable_feature_flag— enabling something that's already enabled has no additional effect
Combining Annotations
The three annotations are independent booleans and can be combined freely. Some combinations that are meaningful in practice:
- readOnly + idempotent: A pure query. Completely safe. Example:
get_user_by_id. - destructive + idempotent: A permanent action that's safe to retry. Example:
delete_user— destructive (the user is gone) but idempotent (deleting an already-deleted user has the same result). Requires confirmation, but can be retried without extra danger. - not destructive + not idempotent: A write that accumulates. Example:
append_to_log— not destructive (data is added, not removed), but calling it twice appends twice. Should be called carefully but doesn't require the same urgency as a destructive action.
A Complete Annotated Tool Set
Here is a realistic example: a filesystem MCP server with correct annotations on every tool.
// Safe read-only tool — no confirmation needed
{
"name": "read_file",
"description": "Read the contents of a file",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Absolute path to the file"}
},
"required": ["path"]
},
"annotations": {
"readOnlyHint": true,
"destructiveHint": false,
"idempotentHint": true
}
}
// Dangerous irreversible tool — must confirm
{
"name": "delete_file",
"description": "Permanently delete a file",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Absolute path to the file to delete"}
},
"required": ["path"]
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false
}
}
// Safe write — idempotent, not destructive
{
"name": "set_config",
"description": "Set a configuration value",
"inputSchema": {
"type": "object",
"properties": {
"key": {"type": "string"},
"value": {"type": "string"}
},
"required": ["key", "value"]
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": false,
"idempotentHint": true
}
}
// Dangerous and idempotent — destructive but safe to retry
{
"name": "delete_user",
"description": "Permanently delete a user account",
"inputSchema": {
"type": "object",
"properties": {
"user_id": {"type": "string"}
},
"required": ["user_id"]
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": true
}
}
How Clients Use Annotations
The current reference implementation is Claude Desktop, which uses annotations to decide whether to surface a confirmation dialog before running a tool. When a tool has destructiveHint: true, Claude Desktop will pause and show the user the tool name, its arguments, and ask for explicit approval before proceeding.
Future clients may do more: color-coded tool lists (green for read-only, red for destructive), automatic retry logic for idempotent tools, or audit logs that flag destructive calls. The annotation system is designed to be forward-compatible — adding annotations now means your server is ready for more sophisticated clients without changes.
To understand the full structure of what belongs in a tool definition, see the complete MCP tool schema guide.
The Critical Caveat: Hints, Not Enforcement
This cannot be overstated: annotations are not a security mechanism. The MCP spec is explicit on this point. A malicious server could declare delete_all_files as readOnlyHint: true. A poorly written server might have incorrect annotations through simple developer error.
Clients should use annotations to optimize UX — reducing unnecessary confirmation friction for safe tools — but they cannot use annotations as the sole basis for access control decisions. Real MCP security operates at the trust level model, not at the annotation level.
Think of annotations like a restaurant menu description: "mild" tells you the dish probably isn't spicy, but you might still want to ask the waiter if you have allergies. It's helpful information, not a guarantee.
Frequently Asked Questions
No. MCP tool annotations are hints, not enforcement. The spec explicitly states that clients cannot rely on annotations as security boundaries — a malicious or poorly written server could declare a destructive tool as readOnly. Annotations should be used to improve UX (deciding when to show confirmation dialogs) but never as the sole basis for access control decisions.
Without annotations, clients have no metadata to guide their behavior. They typically default to treating unannounced tools as potentially dangerous and may prompt the user for confirmation before every tool call, which creates friction. Adding correct annotations — especially readOnlyHint: true for safe tools — reduces unnecessary confirmation dialogs and improves the user experience significantly.
Yes, and it's a common combination. A delete_user tool is destructive (it removes data permanently) but also idempotent (deleting an already-deleted user produces the same outcome as deleting an existing one — the user is gone). Setting destructiveHint: true and idempotentHint: true together tells clients the operation is permanent but safe to retry without causing additional harm.
Tool annotations were introduced in the MCP spec update of March 26, 2025 (version 2025-03-26). They were not part of the original November 2024 MCP release. If you are running an older MCP server implementation that predates this update, you will need to upgrade to the 2025-03-26 spec to use the annotations field.