[{'Text': '"""MCP tools module with auto-discovery and registration.\n\nThis module registers ALL tools at server startup.\nTool names are prefixed with the module name (e.g. database_tools_describe_table)\nfor easier agent understanding of which tool file provides each capability.\nPermission-based access control is handled by PermissionMiddleware\nwhich filters the tool list per-user via the on_list_tools hook.\n\nAccess Levels (enforced by middleware):\n1. Unauthenticated → Only @public tools visible\n2. Authenticated (not ptAdmin) → @public + matching @requires_permissions\n3. ptAdmin → All tools visible\n\nUsage:\n    from point_topic_mcp.tools import get_all_tools, get_tool_function\n\n    # Get all tool functions (returns list of (name, func) tuples)\n    tools = get_all_tools()\n\n    # Get a specific tool by prefixed name\n    tool_func = get_tool_function("database_tools_execute_query")\n"""\n\nimport importlib\nimport pkgutil\nimport inspect\nfrom pathlib import Path\nfrom typing import Callable\n\n# Cache: list of (prefixed_name, func) tuples, and lookup dict\n_discovered_tools: list[tuple[str, Callable]] | None = None\n_tool_lookup: dict[str, Callable] | None = None\n\n\ndef _discover_tools() -> tuple[list[tuple[str, Callable]], dict[str, Callable] | None]:\n    """Discover all tool functions in the tools directory.\n\n    Tool names are prefixed with module name (e.g. database_tools_describe_table).\n\n    Returns:\n        Tuple of (list of (name, func) pairs, dict mapping prefixed names to functions)\n    """\n    global _discovered_tools, _tool_lookup\n\n    if _discovered_tools is not None:\n        return _discovered_tools, _tool_lookup\n\n    tools: list[tuple[str, Callable]] = []\n    lookup: dict[str, Callable] = {}\n\n    # Get the tools package directory\n    tools_dir = Path(__file__).parent\n\n    # Discover all Python modules in this directory\n    for module_info in pkgutil.iter_modules([str(tools_dir)]):\n        # Skip __init__ and any private modules\n        if module_info.name.startswith("_"):\n            continue\n\n        try:\n            # Import the module\n            module = importlib.import_module(\n                f".{module_info.name}", package="point_topic_mcp.tools"\n            )\n\n            # Find all functions defined in this module (not imported from elsewhere)\n            for name, obj in inspect.getmembers(module, inspect.isfunction):\n                # Skip private functions\n                if name.startswith("_"):\n                    continue\n\n                # Only register functions actually defined in this module\n                if obj.__module__ == module.__name__:\n                    prefixed_name = f"{module_info.name}_{name}"\n                    tools.append((prefixed_name, obj))\n                    lookup[prefixed_name] = obj\n\n        except Exception as e:\n            import traceback\n            traceback.print_exc()\n            raise RuntimeError(\n                f"[MCP] Tool module \'{module_info.name}\' failed to import: {e}. "\n                "Fix the module — the server will not start with broken tools."\n            ) from e\n\n    _discovered_tools = tools\n    _tool_lookup = lookup\n\n    return tools, lookup\n\n\ndef get_all_tools() -> list[tuple[str, Callable]]:\n    """Get all discovered tool functions with their prefixed names.\n\n    Returns:\n        List of (prefixed_name, func) tuples\n    """\n    tools, _ = _discover_tools()\n    return tools\n\n\ndef get_tool_function(name: str) -> Callable | None:\n    """Get a specific tool function by prefixed name.\n\n    Args:\n        name: Prefixed tool name (e.g. database_tools_execute_query)\n\n    Returns:\n        The tool function if found, None otherwise\n    """\n    _, lookup = _discover_tools()\n    if lookup is None:\n        return None\n    return lookup.get(name)\n\n\ndef register_tools(mcp):\n    """Register all MCP tools by auto-discovering functions in tool modules.\n\n    Automatically finds all Python modules in the tools directory and registers\n    every public function (not starting with _) as an MCP tool.\n\n    ALL tools are registered - access control is handled by PermissionMiddleware\n    which filters the visible tool list per-user when clients request tools/list.\n\n    Just create a .py file with tool functions - the middleware handles permissions!\n\n    Args:\n        mcp: The MCP server instance (FastMCP or official SDK Server)\n    """\n    # Import here to avoid circular imports\n    try:\n        from point_topic_mcp.auth import PermissionMiddleware\n        from point_topic_mcp.auth.decorators import PUBLIC_TOOLS, TOOL_PERMISSIONS\n\n        mcp.add_middleware(PermissionMiddleware())\n        print("[MCP] Permission middleware enabled")\n\n        # Migrate auth registries to use prefixed names (middleware checks by tool name)\n        tools, _ = _discover_tools()\n        for prefixed_name, tool_func in tools:\n            name = tool_func.__name__\n            if name in PUBLIC_TOOLS:\n                PUBLIC_TOOLS.add(prefixed_name)\n            if name in TOOL_PERMISSIONS:\n                TOOL_PERMISSIONS[prefixed_name] = TOOL_PERMISSIONS[name]\n    except Exception as e:\n        # Middleware not available (e.g., local dev without auth)\n        print(f"[MCP] Permission middleware skipped: {e}")\n\n    tools, _ = _discover_tools()\n\n    for prefixed_name, tool_func in tools:\n        mcp.tool(name=prefixed_name)(tool_func)\n        print(f"[MCP] Registered tool: {prefixed_name}")\n\n    print(f"[MCP] Total tools registered: {len(tools)}")\n\n\n__all__ = ["register_tools", "get_all_tools", "get_tool_function"]\n'}]