Skip to content

Feature: enforce tool annotation hints (readOnlyHint/destructiveHint/idempotentHint) #2937

@tcconnally

Description

@tcconnally

Feature Request: Enforce tool annotation hints (readOnlyHint/destructiveHint/idempotentHint)

Problem

The MCP spec defines three tool annotation hints (readOnlyHint, destructiveHint, idempotentHint) to help clients understand tool behavior. The python-sdk accepts these annotations on tool registration but does nothing with them — they're passed through to list_tools responses as metadata only.

This means:

  1. A tool marked destructiveHint=True can still be called without any confirmation or gating
  2. A tool marked readOnlyHint=True could modify state without the SDK detecting the mismatch
  3. There's no validation that annotations match actual tool behavior

Real-world Impact

I maintain two MCP servers (Perseus, 27+ tools; Mimir, 26+ tools) that extensively use these annotations. We mark tools that write to memory as destructiveHint=True and tools that only read as readOnlyHint=True. But the annotations are purely advisory — the python-sdk doesn't help us enforce them.

Proposed Solution

Add an optional configuration that enables annotation-based gating:

server = Server(
    "my-server",
    enforce_tool_hints=True  # opt-in
)

@server.tool(readOnlyHint=True, destructiveHint=False)
async def search_docs(query: str) -> list[str]:
    # SDK could validate this doesn't call any write operations
    ...

At minimum:

  1. Log a warning when a destructiveHint=True tool is called (security boundary)
  2. Option to require explicit client confirmation before destructive tool calls
  3. Optional validation hook: a callback that fires before destructive tool execution

Why This Matters

Without SDK-level enforcement, tool annotations are documentation — not security boundaries. Every MCP server implementor has to build their own enforcement layer, leading to inconsistent behavior across the ecosystem.

Happy to Contribute

I'm willing to submit a PR if the design direction is acceptable. Would propose starting with a simple destructive-tool logging warn + optional confirmation gate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions