Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.praison.ai/llms.txt

Use this file to discover all available pages before exploring further.

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

HintTypeDefaultDescription
readOnlyHintbooleanfalseTool only reads data, doesn’t modify state
destructiveHintbooleantrueTool may have destructive/irreversible effects
idempotentHintbooleanfalseTool can be called multiple times with same result
openWorldHintbooleantrueTool interacts with external world (network, APIs)

Code Usage

Default Annotations

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

# 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

# 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

# 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

# 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:
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:
{
  "name": "example.tool",
  "description": "Example",
  "inputSchema": {"type": "object"},
  "annotations": {
    "readOnlyHint": true,
    "destructiveHint": false,
    "idempotentHint": false,
    "openWorldHint": true
  }
}

Custom Title Annotation

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

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:
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

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