"""Built-in tool that returns the detailed manual for a given tool."""
from pathlib import Path
from ._internal.base import Tool, ToolContext, ToolResult
MANUALS_DIR = Path(__file__).parent.parent.parent / "manuals"
class ToolManualTool(Tool):
name = "tool_manual"
description = (
"Returns the detailed manual for a tool: full usage instructions, parameter reference, and examples. "
"Call this before using an unfamiliar tool, or when you are unsure about the correct format or parameters."
)
parameters = {
"type": "object",
"properties": {
"tool_name": {
"type": "string",
"description": "Name of the tool to look up, e.g. 'filesystem'",
}
},
"required": ["tool_name"],
}
def __init__(self, registry=None) -> None:
self._registry = registry
async def execute(self, params: dict, ctx: ToolContext | None = None) -> ToolResult:
tool_name = params["tool_name"].strip()
manual_file = MANUALS_DIR / f"{tool_name}.md"
if manual_file.exists():
return ToolResult(success=True, output=manual_file.read_text(encoding="utf-8"))
# No .md file — generate a manual from the tool's schema
if self._registry:
try:
tool = self._registry.get(tool_name)
return ToolResult(success=True, output=_auto_manual(tool))
except Exception:
pass
available = sorted(f.stem for f in MANUALS_DIR.glob("*.md")) if MANUALS_DIR.exists() else []
hint = f"\nAvailable manuals: {', '.join(available)}" if available else ""
return ToolResult(success=False, output=f"No manual found for '{tool_name}'.{hint}", error="not_found")
def _auto_manual(tool) -> str:
"""Generate a readable manual from a tool's schema."""
lines = [f"# {tool.name}", "", tool.description, "", "## Parameters"]
props = tool.parameters.get("properties", {})
required = set(tool.parameters.get("required", []))
if not props:
lines.append("This tool takes no parameters.")
else:
for param_name, spec in props.items():
req = " (required)" if param_name in required else " (optional)"
ptype = spec.get("type", "any")
desc = spec.get("description", "")
enum = spec.get("enum")
line = f"- `{param_name}` ({ptype}{req}): {desc}"
if enum:
line += f" — one of: {', '.join(repr(e) for e in enum)}"
lines.append(line)
# Nested object properties
nested = spec.get("properties", {})
for nname, nspec in nested.items():
nreq = " (required)" if nname in spec.get("required", []) else " (optional)"
ndesc = nspec.get("description", "")
lines.append(f" - `{nname}` ({nspec.get('type', 'any')}{nreq}): {ndesc}")
return "\n".join(lines)