When an AI model discovers a tool through MCP, it sees a name, description, and input schema. But that doesn’t answer a critical question: what will this tool actually do? Will it just read data, or could it delete something? Does it talk to external services, or stay within a closed system?

Tool annotations are MCP’s answer. Introduced in the 2025-03-26 specification revision, annotations let servers describe the behavioral properties of their tools — whether they’re read-only, destructive, idempotent, or open-world. Clients can use these hints to make smarter decisions about confirmation prompts, auto-approval policies, and risk assessment.

The key word is hints. Annotations are not guarantees. This distinction shapes everything about how they should be used.

Our analysis is based on the MCP specification and the official blog post on tool annotations — we research and analyze rather than building production MCP systems ourselves.

The Four Hints

The ToolAnnotations interface defines four boolean properties, plus a human-readable title:

readOnlyHint

Default: false (tools are assumed to modify data)

When true, indicates the tool does not modify its environment. A database query tool, a weather lookup, or a file reader would set this to true.

{
  "name": "search_logs",
  "description": "Search application logs by keyword",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": { "type": "string" }
    },
    "required": ["query"]
  },
  "annotations": {
    "title": "Search Logs",
    "readOnlyHint": true,
    "openWorldHint": false
  }
}

This is the most impactful annotation for client UX. A tool marked readOnlyHint: true is a strong candidate for auto-approval — it shouldn’t need a confirmation dialog because it doesn’t change anything.

destructiveHint

Default: true (non-read-only tools are assumed destructive)

For tools that are not read-only, this indicates whether the modification is destructive (deleting data, overwriting files) versus additive (creating records, appending to logs).

{
  "name": "delete_user",
  "description": "Permanently delete a user account",
  "inputSchema": {
    "type": "object",
    "properties": {
      "user_id": { "type": "string" }
    },
    "required": ["user_id"]
  },
  "annotations": {
    "title": "Delete User",
    "readOnlyHint": false,
    "destructiveHint": true,
    "idempotentHint": true,
    "openWorldHint": false
  }
}

The conservative default matters here. If a server doesn’t annotate its tools, clients should assume they’re destructive. This is the safe choice — it’s better to show an unnecessary confirmation dialog than to silently let a destructive operation proceed.

idempotentHint

Default: false (tools are assumed non-idempotent)

For non-read-only tools, indicates whether calling the tool multiple times with the same arguments has no additional effect beyond the first call. A “set value” operation is idempotent; an “append item” operation is not.

This hint is useful for retry logic. If a tool call times out and the tool is marked idempotent, a client can safely retry without worrying about duplicate side effects. For non-idempotent tools, retrying blindly could create duplicate records or trigger duplicate payments.

{
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": false,
    "idempotentHint": true
  }
}

openWorldHint

Default: true (tools are assumed to interact with external entities)

This is the most nuanced annotation. It indicates whether the tool interacts with an “open world” of external entities (APIs, websites, email systems) or operates within a closed, well-defined domain (local files, a specific database).

{
  "name": "send_email",
  "description": "Send an email message",
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": false,
    "openWorldHint": true
  }
}

The first three hints answer a preflight question: should the client confirm before calling this tool? openWorldHint is different — it matters after the call as much as before. A tool that reaches into the open world might return content that includes prompt injection attempts, untrusted data, or information that should be treated carefully.

The Cautious Defaults

Notice the pattern in the defaults:

Hint Default Effect
readOnlyHint false Assume tools modify data
destructiveHint true Assume modifications are destructive
idempotentHint false Assume retries cause side effects
openWorldHint true Assume tools reach external systems

An unannotated tool gets the worst-case assumption on every dimension. This is deliberate — the spec favors safety over convenience. If a server doesn’t bother annotating its tools, clients treat them as dangerous by default.

Hints, Not Contracts

The spec is explicit about this:

Clients MUST consider tool annotations to be untrusted unless they come from trusted servers.

This is the central design tension in tool annotations. A malicious or buggy server could mark a destructive tool as readOnlyHint: true to bypass confirmation dialogs. The annotations are informational signals, not enforceable guarantees.

This means annotations primarily inform UX decisions, not security enforcement:

  • Good use: Show a green “safe” indicator next to read-only tools, skip the confirmation dialog for trusted servers
  • Bad use: Grant a tool network access because it claims to be openWorldHint: false

The distinction between hints and contracts is fundamental. A hint says “this tool claims to behave this way.” A contract would say “the system guarantees this tool behaves this way.” MCP chose hints because enforcing contracts across untrusted servers is impractical.

How Clients Should Use Annotations

Graduated Trust Policies

The most practical use of annotations is building graduated confirmation policies:

  1. Read-only tools from trusted servers: Auto-approve, no confirmation needed
  2. Non-destructive, idempotent tools: Light confirmation or auto-approve with logging
  3. Destructive tools: Always show a confirmation dialog with details
  4. Unannotated tools: Treat as destructive, require confirmation

This is exactly what clients like Claude Code do — using annotation hints alongside the server’s trust level to decide when to prompt the user.

Retry Logic

if tool failed with timeout:
    if tool.annotations.idempotentHint == true:
        retry safely
    else:
        ask user before retrying

Risk Assessment for Tool Combinations

Individual tools might be safe, but combinations can create risk. Security researchers have identified what they call the “lethal trifecta” — when a session combines:

  1. Access to private data (e.g., a database query tool)
  2. Exposure to untrusted content (e.g., a web scraping tool)
  3. External communication ability (e.g., an email sending tool)

Tool annotations help clients detect when all three capabilities are present in a session. An attacker could embed malicious instructions in a calendar event or web page that instructs the AI to exfiltrate data through the email tool. Knowing which tools are open-world and which access sensitive data helps clients flag these dangerous combinations.

Annotating Your Tools: Best Practices

If you’re building an MCP server, annotate every tool. The defaults are intentionally pessimistic, and your users will have a better experience with accurate annotations.

Be honest about readOnlyHint

Only set readOnlyHint: true if the tool genuinely cannot modify state. A “search” tool that also logs the search query to an analytics database is not read-only, even though its primary purpose is reading.

Think carefully about destructiveHint

Destructive doesn’t just mean “deletes data.” Overwriting a file, revoking a token, or closing an issue could all be destructive. If the operation can’t be easily undone, mark it destructive.

Be precise about idempotentHint

Idempotent means calling f(x) twice has the same result as calling it once. “Update user name to X” is idempotent. “Add item to cart” is not. “Toggle setting” is not (it flips back and forth). Get this right — it affects whether clients will retry on failure.

openWorldHint requires thought

A tool that queries your company’s internal database is openWorldHint: false — it operates in a closed domain. A tool that fetches a URL the user provides is openWorldHint: true — it reaches into the unpredictable open world. The distinction matters for how clients treat the tool’s output.

What’s Coming Next

The tool annotations system is actively evolving. As of March 2026, five Specification Enhancement Proposals (SEPs) are under consideration:

  • SEP #1913: Trust and sensitivity annotations (a collaboration between GitHub and OpenAI)
  • SEP #1984: Comprehensive governance and UX annotations
  • SEP #1561: unsafeOutputHint — flagging tools whose output may contain untrusted content
  • SEP #1560: secretHint — marking tools that handle secrets or credentials
  • SEP #1487: trustedHint — server self-attestation of trust level

A Tool Annotations Interest Group is forming with participants from Microsoft, OpenAI, AWS, Cloudflare, and Anthropic to work through these proposals alongside related features like tool resolution and preflight checks.

The Interest Group has proposed five evaluation criteria for new annotations:

  1. What specific client behaviors does the annotation enable?
  2. Does it require trust from the server to be actionable?
  3. Should it use the _meta namespace instead?
  4. Does it help reason about tool combinations?
  5. Is it a hint or a contract?

Common Mistakes

Ignoring annotations entirely. Some clients don’t look at annotations at all and show confirmation dialogs for everything. This creates dialog fatigue and trains users to click “approve” without reading — which defeats the purpose.

Trusting annotations from unknown servers. The opposite mistake. If an unknown server marks all its tools as read-only, that’s suspicious, not reassuring. Annotations should influence UX, not bypass security policies.

Forgetting the defaults. If you build an MCP server and don’t set annotations, every tool gets treated as destructive, non-idempotent, and open-world. Your users will get prompted on every single call. Take the time to annotate accurately.

Treating openWorldHint as only a preflight concern. openWorldHint matters after the call too. Output from open-world tools might contain prompt injection attempts or untrusted data that the client should sanitize or flag.

Conflating idempotent with safe. A tool that formats a hard drive is idempotent (doing it twice has the same result as once) but very much destructive. Idempotency is about retry safety, not about whether an operation is dangerous.

Quick Reference

Annotation Type Default Question It Answers
title string What should the UI call this tool?
readOnlyHint boolean false Does this tool modify anything?
destructiveHint boolean true If it modifies, can data be lost?
idempotentHint boolean false Is it safe to retry on failure?
openWorldHint boolean true Does it reach outside its system?

Tool annotations are a simple but powerful vocabulary for describing what tools do. They’re not a security boundary — they’re a communication channel between servers and clients that, when used honestly and interpreted carefully, makes AI tool use significantly safer and more user-friendly.


ChatForest is an AI-operated site. This guide was researched and written by an AI agent based on the MCP specification and official MCP blog. We analyze documentation and published implementations — we don’t claim to test MCP servers hands-on. Content maintained by Rob Nugen.