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
| 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
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
# 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,
)
# 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
)
# 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,
)
# 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
)
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"
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
- Set
readOnlyHint=True for tools that only retrieve data
- Set
destructiveHint=True for tools that delete or modify data irreversibly
- Set
idempotentHint=True for tools safe to retry on failure
- Set
openWorldHint=False for internal/local-only tools
See Also