MCPMCP Python SDK 2.2 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
24 small wins to finish your pathNext lesson

Tool annotations: telling hosts what a tool does

Looking an order up is harmless; refunding one moves money. Annotations let a server say which is which, so a host can ask a person first.

Exampleshop.py, new tool
@mcp.tool(
    title="Refund an order",
    annotations=ToolAnnotations(read_only_hint=False, destructive_hint=False, idempotent_hint=False),
)
def refund_order(order_id: str, reason: str) -> str:
    """Refund the full total of an order to the customer's card."""
    if order_id not in ORDERS:
        raise ToolError(f"No order with id {order_id!r}.")
    return f"Refunded {ORDERS[order_id]['total']:.2f} for order {order_id}: {reason}."

The imports gain ToolAnnotations from mcp.types. title is a name for people, shown in place of refund_order. The annotations are hints about behaviour:

  • read_only_hint: the tool changes nothing. lookup_order now sets it to True.
  • destructive_hint: an update may delete or overwrite something. A refund adds a transaction rather than destroying data, so False.
  • idempotent_hint: calling twice with the same arguments has no more effect than once. Two refund calls would be two refunds, so False.
  • open_world_hint, not set here: the tool reaches outside systems, like the web.
Example
import asyncio

from mcp import Client
from shop import mcp


async def main():
    async with Client(mcp) as client:
        tools = await client.list_tools()
        for tool in tools.tools:
            hints = tool.annotations
            read_only = hints.read_only_hint if hints else None
            print(f"{tool.name:14} title={tool.title!r:20} read_only={read_only}")


asyncio.run(main())

search_help has no annotations at all, so a careful host has to assume the worst about it. Claude Code and other hosts ask for approval before calling tools according to their own settings, and hints like these are what they can base that on.

Hints, not security
An annotation is the server describing itself. A host may ignore it, and a malicious server can lie in it. Anything that must not happen without approval has to be enforced by your own code, which lesson 14 does for refunds.
Try it yourself
  • Mark search_help as read-only and list the tools again.
  • Add open_world_hint=False to lookup_order.
  • Print tool.annotations for refund_order.

You understood something today that you didn't yesterday.