What is ToolAnnotations?
ToolAnnotations is an optional hints object attached to a Tool definition that communicates the expected side-effect profile of a tool to MCP clients and hosts. It lives inside the Tool's metadata — alongside the tool's name, description, and inputSchema — and is transmitted as part of the tools/list response. Because annotations are purely advisory, they carry no enforcement weight at the protocol level; a server may declare readOnlyHint: true on a tool that internally writes to a database, and the protocol will not reject it. The contract is behavioural: violating declared hints breaks client trust and can cause silent data-loss bugs in automation pipelines that rely on those hints to skip confirmation prompts. \n\n In the MCP message flow, ToolAnnotations reach the client once during capability negotiation or on demand via tools/list. Clients and hosts use the four boolean fields — readOnlyHint, destructiveHint, idempotentHint, and openWorldHint — to drive UI decisions: whether to show a confirmation dialog, auto-approve a tool call in an agentic loop, surface a destructive-action warning, or flag that a tool may contact external systems beyond the server's own scope. The optional title field provides a human-readable display name that can override the raw tool name in host interfaces. \n\n ToolAnnotations were introduced to address the gap between a tool's JSON Schema (which describes inputs) and its behavioural contract (which describes consequences). The four hint fields are intentionally orthogonal: a tool can be both idempotent and destructive (safe to retry, but still causes permanent change), or both read-only and open-world (reads from an external API). Defaults are conservative — omitting readOnlyHint implies the tool may write; omitting destructiveHint implies it may be destructive — so servers should explicitly set hints rather than rely on defaults when a tool is genuinely safe.
When to use
On every Tool, to help clients build better UX (auto-approve safe operations, warn before destructive ones).
When NOT to use
Never to deceive — lying with annotations breaks the user-consent model.
Notes
Defaults bias toward caution
When any boolean hint is omitted, the MCP spec defines the default as the more restrictive interpretation: readOnlyHint defaults to false (tool may mutate), destructiveHint defaults to true (tool may destroy data), idempotentHint defaults to false (repeated calls may differ), and openWorldHint defaults to true (tool may contact external systems). This means a tool with an empty annotations object is treated as potentially dangerous and non-idempotent, which is the right conservative posture for hosts building auto-approval logic.
Hints do not replace input validation
A common production pitfall is treating ToolAnnotations as a security boundary. Because the server itself provides the annotations, a compromised or malicious server can lie about them. Hosts must not use readOnlyHint: true as grounds for skipping authorization checks or audit logging. Annotations are a UX and automation-flow hint, not a sandboxing mechanism — enforce actual side-effect constraints at the OS, database, or network layer.
idempotentHint enables safe agentic retry
Setting idempotentHint: true is a strong signal to orchestrators that they may retry the tool call after a transient failure without risk of duplicating effects. This is particularly valuable in multi-step agentic pipelines where network errors are common. Only set this flag if calling the tool twice with identical arguments is truly equivalent to calling it once — operations like file appends or message sends should never carry this hint even if they appear stateless from the outside.
title vs tool name in host UIs
The title field in ToolAnnotations is a display-only label intended for host interfaces that render tool pickers or confirmation dialogs. It does not affect routing — the tool is still identified by its name field on the Tool object. If title is absent, hosts fall back to the tool name. Keep title concise (under 60 characters) because many host UIs truncate long strings in modal dialogs and command palettes.
SDK support varies across languages
As of mid-2025, the TypeScript MCP SDK exposes ToolAnnotations as a first-class typed object on the Tool interface, and the Python SDK surfaces it as an optional TypedDict. Some older SDK versions (pre-1.0 for both languages) accepted annotations as an untyped dict and performed no schema validation on the boolean fields. If your server targets clients that may be running older SDK versions, ensure annotations values are strict JSON booleans rather than truthy strings to avoid silent type coercion issues.
Fields
| Field | Type | Required | Purpose |
|---|---|---|---|
| title | string? | no | Display name override for the tool. |
| readOnlyHint | boolean? | no | Tool does not modify state. |
| destructiveHint | boolean? | no | Tool may cause irreversible side-effects. |
| idempotentHint | boolean? | no | Repeated calls have the same effect. |
| openWorldHint | boolean? | no | Tool interacts with external entities (internet, APIs). |
Examples
Hints for a search tool
{
"readOnlyHint": true,
"idempotentHint": true,
"openWorldHint": true
}
Common mistakes
❌ Skipping annotations entirely
✅ Always annotate — clients use hints to surface dangerous operations.