> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Tool Annotations Module

> MCP 2025-11-25 tool annotation hints for behavioral metadata

# MCP Tool Annotations Module

Tool annotations provide behavioral hints to MCP clients about how tools behave. This follows the MCP 2025-11-25 specification.

## Annotation Hints

| Hint              | Type    | Default | Description                                        |
| ----------------- | ------- | ------- | -------------------------------------------------- |
| `readOnlyHint`    | boolean | `false` | Tool only reads data, doesn't modify state         |
| `destructiveHint` | boolean | `true`  | Tool may have destructive/irreversible effects     |
| `idempotentHint`  | boolean | `false` | Tool can be called multiple times with same result |
| `openWorldHint`   | boolean | `true`  | Tool interacts with external world (network, APIs) |

## Code Usage

### Default Annotations

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.mcp_server.registry import MCPToolDefinition

# Tool with default annotations
tool = MCPToolDefinition(
    name="example.tool",
    description="Example tool",
    handler=lambda: "result",
    input_schema={"type": "object"}
)

# Check defaults
print(tool.read_only_hint)      # False
print(tool.destructive_hint)    # True
print(tool.idempotent_hint)     # False
print(tool.open_world_hint)     # True
```

### Read-Only Tool

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Tool that only reads data
read_tool = MCPToolDefinition(
    name="memory.show",
    description="Show memory contents without modification",
    handler=show_memory,
    input_schema={"type": "object"},
    read_only_hint=True,
    destructive_hint=False,
)
```

### Destructive Tool

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Tool that deletes data
delete_tool = MCPToolDefinition(
    name="file.delete",
    description="Delete a file permanently",
    handler=delete_file,
    input_schema={
        "type": "object",
        "properties": {
            "path": {"type": "string", "description": "File path"}
        },
        "required": ["path"]
    },
    destructive_hint=True,
    idempotent_hint=False,  # Deleting twice has different effects
)
```

### Idempotent Tool

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Tool that can be safely retried
config_tool = MCPToolDefinition(
    name="config.set",
    description="Set configuration value",
    handler=set_config,
    input_schema={
        "type": "object",
        "properties": {
            "key": {"type": "string"},
            "value": {"type": "string"}
        }
    },
    idempotent_hint=True,
    destructive_hint=False,
)
```

### Closed-World Tool

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Tool that doesn't interact with external systems
session_tool = MCPToolDefinition(
    name="session.get",
    description="Get session data (internal only)",
    handler=get_session,
    input_schema={"type": "object"},
    read_only_hint=True,
    destructive_hint=False,
    open_world_hint=False,  # No external interaction
)
```

## Tool Schema Output

Annotations are included in the MCP schema:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
tool = MCPToolDefinition(
    name="example.tool",
    description="Example",
    handler=lambda: None,
    input_schema={"type": "object"},
    read_only_hint=True,
    destructive_hint=False,
)

schema = tool.to_mcp_schema()
print(schema)
```

**Output:**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "name": "example.tool",
  "description": "Example",
  "inputSchema": {"type": "object"},
  "annotations": {
    "readOnlyHint": true,
    "destructiveHint": false,
    "idempotentHint": false,
    "openWorldHint": true
  }
}
```

## Custom Title Annotation

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
tool = MCPToolDefinition(
    name="praisonai.workflow.run",
    description="Execute a PraisonAI workflow",
    handler=run_workflow,
    input_schema={"type": "object"},
    title="Run AI Workflow",  # Human-friendly title
)

schema = tool.to_mcp_schema()
print(schema["annotations"]["title"])  # "Run AI Workflow"
```

## Category and Tags

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
tool = MCPToolDefinition(
    name="web.search",
    description="Search the web",
    handler=web_search,
    input_schema={"type": "object"},
    category="web",
    tags=["search", "internet", "query"],
    read_only_hint=True,
)

print(tool.category)  # "web"
print(tool.tags)      # ["search", "internet", "query"]
```

## Automatic Hint Inference

The registry bridge can infer hints from tool names:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.mcp_server.adapters.tools_bridge import _infer_tool_hints

# Read-only patterns: show, list, get, read, search, find, query
hints = _infer_tool_hints("memory.show")
print(hints["read_only_hint"])  # True

# Idempotent patterns: set, update, configure
hints = _infer_tool_hints("config.set")
print(hints["idempotent_hint"])  # True

# Closed-world patterns: memory, session, config, local
hints = _infer_tool_hints("session.create")
print(hints["open_world_hint"])  # False
```

## Searching by Annotations

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.mcp_server.registry import MCPToolRegistry

registry = MCPToolRegistry()
# ... register tools ...

# Find all read-only tools
results, _, total = registry.search(read_only=True)
print(f"Found {total} read-only tools")

# Find tools by category
results, _, total = registry.search(category="memory")
```

## Best Practices

1. **Set `readOnlyHint=True`** for tools that only retrieve data
2. **Set `destructiveHint=True`** for tools that delete or modify data irreversibly
3. **Set `idempotentHint=True`** for tools safe to retry on failure
4. **Set `openWorldHint=False`** for internal/local-only tools

## See Also

* [MCP Tool Annotations CLI](/docs/cli/mcp-tool-annotations) - CLI commands for tool info
* [MCP Tool Search](/docs/mcp/mcp-tool-search) - Search tools by annotations
* [MCP Server](/docs/mcp/mcp-server) - Core MCP server documentation
