Build a loop-correct sync Tool that dispatches to the connection loop.
(self, server: str, mcp_tool: Any, client: Any)
| 114 | return True |
| 115 | |
| 116 | def _wrap(self, server: str, mcp_tool: Any, client: Any) -> Any: |
| 117 | """Build a loop-correct sync Tool that dispatches to the connection loop.""" |
| 118 | from src.services.mcp.mcp_string_utils import build_mcp_tool_name |
| 119 | from src.tool_system.build_tool import build_tool |
| 120 | from src.tool_system.protocol import ToolResult |
| 121 | |
| 122 | full = build_mcp_tool_name(server, mcp_tool.name) |
| 123 | schema = getattr(mcp_tool, "input_schema", None) or {"type": "object", "properties": {}} |
| 124 | loop = self._loop |
| 125 | |
| 126 | def _call(args: dict[str, Any], ctx: Any) -> ToolResult: |
| 127 | try: |
| 128 | fut = asyncio.run_coroutine_threadsafe(client.call_tool(mcp_tool.name, args), loop) |
| 129 | res = fut.result(_CALL_TIMEOUT_S) |
| 130 | except Exception as exc: # noqa: BLE001 |
| 131 | return ToolResult(name=full, output=f"MCP call failed: {exc}", is_error=True) |
| 132 | return ToolResult( |
| 133 | name=full, |
| 134 | output=_render_content(getattr(res, "content", None)), |
| 135 | is_error=bool(getattr(res, "is_error", False)), |
| 136 | ) |
| 137 | |
| 138 | return build_tool( |
| 139 | name=full, |
| 140 | input_schema=schema, |
| 141 | call=_call, |
| 142 | description=getattr(mcp_tool, "description", None) or f"MCP tool {full}", |
| 143 | is_mcp=True, |
| 144 | ) |
| 145 | |
| 146 | def shutdown(self) -> None: |
| 147 | """Disconnect clients and stop the background loop. Idempotent.""" |
no test coverage detected