""" tools/mcp_proxy_tool.py — Dynamic BaseTool wrapper for external MCP server tools. One McpProxyTool instance is created per tool discovered from each MCP server. The tool name is namespaced as mcp__{server_slug}__{tool_name} to avoid collisions. """ from __future__ import annotations import re from .base import BaseTool, ToolResult def _slugify(name: str) -> str: """Convert a server name to a safe identifier component.""" return re.sub(r"[^a-z0-9]+", "_", name.lower()).strip("_") class McpProxyTool(BaseTool): requires_confirmation = False allowed_in_scheduled_tasks = True def __init__( self, server_id: str, server_name: str, server: dict, tool_name: str, description: str, input_schema: dict, ) -> None: self.name = f"mcp__{_slugify(server_name)}__{tool_name}" self.description = f"[{server_name}] {description}" self.input_schema = input_schema if input_schema else { "type": "object", "properties": {}, "required": [] } self._server_id = server_id self._server_display_name = server_name # human-readable name for UI self._server = server # full server dict with decrypted secrets self._remote_tool_name = tool_name # original name on the MCP server async def execute(self, **kwargs) -> ToolResult: from ..mcp_client.manager import call_tool # Refresh server config (api_key may have changed since startup) from ..mcp_client.store import get_server server = await get_server(self._server_id, include_secrets=True) or self._server return await call_tool(server, self._remote_tool_name, kwargs)