Newer
Older
navi-1 / navi / tools / reload_tools.py
"""Built-in tool to hot-reload user tools and context providers without restarting."""

from navi.config import settings

from .base import Tool, ToolResult


class ReloadToolsTool(Tool):
    name = "reload_tools"
    description = (
        "Hot-reload all tools from the tools/ directory, context providers from "
        "context_providers/, and reconnect all configured MCP servers without "
        "restarting the server. Call this after writing or editing a tool, context "
        "provider, or MCP server configuration. Returns a report of what was loaded "
        "and any errors per file or server."
    )
    parameters = {
        "type": "object",
        "properties": {},
        "required": [],
    }

    def __init__(self, registry=None, cp_registry=None, mcp_manager=None) -> None:
        self._registry = registry
        self._cp_registry = cp_registry
        self._mcp_manager = mcp_manager

    async def execute(self, params: dict) -> ToolResult:
        if self._registry is None:
            return ToolResult(success=False, output="Tool registry not available.", error="no_registry")

        lines = []
        has_errors = False

        tool_result = self._registry.reload_user_tools(settings.tools_dir)
        if tool_result.loaded:
            lines.append(f"Tools ({len(tool_result.loaded)}): {', '.join(t.name for t in tool_result.loaded)}")
        else:
            lines.append("Tools: none.")
        if tool_result.errors:
            has_errors = True
            lines.append(f"Tool errors ({len(tool_result.errors)}):")
            for filename, error in tool_result.errors.items():
                lines.append(f"  {filename}: {error}")

        if self._cp_registry is not None:
            cp_result = self._cp_registry.reload_user_providers(settings.context_providers_dir)
            if cp_result.loaded:
                lines.append(f"Context providers ({len(cp_result.loaded)}): {', '.join(cp_result.loaded)}")
            else:
                lines.append("Context providers: none.")
            if cp_result.errors:
                has_errors = True
                lines.append(f"Context provider errors ({len(cp_result.errors)}):")
                for filename, error in cp_result.errors.items():
                    lines.append(f"  {filename}: {error}")

        # Reconnect MCP servers
        if self._mcp_manager is not None:
            try:
                await self._mcp_manager.reload_all()
                from navi.api.deps import register_mcp_tools
                await register_mcp_tools(self._registry, self._mcp_manager)
                mcp_tools = [t.name for t in self._registry.all() if t.name.startswith("mcp:")]
                lines.append(f"MCP tools ({len(mcp_tools)}): {', '.join(mcp_tools) or 'none'}")
            except Exception as exc:
                has_errors = True
                lines.append(f"MCP reload error: {exc}")

        return ToolResult(success=not has_errors, output="\n".join(lines))