Newer
Older
navi-1 / navi / tools / list_tools.py
@Eugene Sukhodolskiy Eugene Sukhodolskiy on 13 May 3 KB Add profile_id support to list_tools
"""Built-in tool that returns the current list of available tools."""

from __future__ import annotations

import json
from pathlib import Path

from .base import Tool, ToolResult

_USER_ENABLED_FILE = Path("tools/enabled.json")


def _load_user_enabled_tools() -> list[str]:
    try:
        return json.loads(_USER_ENABLED_FILE.read_text())
    except Exception:
        return []


class ListToolsTool(Tool):
    name = "list_tools"
    description = (
        "Returns the list of tools available to the active profile (or all tools if no profile is given). "
        "Pass profile_id to preview what a specific profile can do — useful before switching profiles."
    )
    parameters = {
        "type": "object",
        "properties": {
            "profile_id": {
                "type": "string",
                "description": "Optional profile ID. When provided, returns only the tools enabled for that profile.",
            }
        },
        "required": [],
    }

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

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

        profile_id = params.get("profile_id")
        if profile_id and self._profile_registry is not None:
            try:
                profile = self._profile_registry.get(profile_id)
            except Exception:
                return ToolResult(success=False, output=f"Profile '{profile_id}' not found.", error="profile_not_found")

            names = list(profile.enabled_tools)
            extra = _load_user_enabled_tools()
            for name in extra:
                if name not in names:
                    names.append(name)

            # Expand MCP server groups into concrete tool names
            if profile.mcp_servers and self._mcp_manager:
                for server_name, groups in profile.mcp_servers.items():
                    if "*" in groups:
                        prefix = f"mcp:{server_name}:"
                        for tool in self._registry.all():
                            if tool.name.startswith(prefix) and tool.name not in names:
                                names.append(tool.name)
                    else:
                        for group_name in groups:
                            for tool_name in self._mcp_manager.resolve_group(server_name, group_name):
                                full_name = f"mcp:{server_name}:{tool_name}"
                                if full_name not in names:
                                    names.append(full_name)

            tools = []
            for name in names:
                try:
                    tools.append(self._registry.get(name))
                except Exception:
                    pass

            lines = [f"Tools for profile '{profile_id}' ({len(tools)} total):"]
            for tool in tools:
                lines.append(f"• {tool.name}: {tool.description}")
            return ToolResult(success=True, output="\n".join(lines))

        # No profile specified — return all tools
        lines = [f"All available tools ({len(self._registry.all())} total):"]
        for tool in self._registry.all():
            lines.append(f"• {tool.name}: {tool.description}")

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