Execute registered tools and return stringified result. Unknown tools return descriptive errors. Fully asynchronous: built-in functions execute directly; MCP path uses async httpx client.
(name: str, arguments_json: str)
| 79 | |
| 80 | |
| 81 | async def execute_tool_call(name: str, arguments_json: str) -> str: |
| 82 | """Execute registered tools and return stringified result. Unknown tools return descriptive errors. |
| 83 | Fully asynchronous: built-in functions execute directly; MCP path uses async httpx client. |
| 84 | """ |
| 85 | try: |
| 86 | params = json.loads(arguments_json or "{}") |
| 87 | except Exception: |
| 88 | params = {} |
| 89 | |
| 90 | func = FUNCTION_REGISTRY.get(name) |
| 91 | if not func: |
| 92 | # If tool is not built-in but declared, try MCP adapter if configured (env or per-request) |
| 93 | if name in _ALLOWED_RUNTIME_TOOLS: |
| 94 | try: |
| 95 | from api_utils.mcp_adapter import ( |
| 96 | execute_mcp_tool, |
| 97 | execute_mcp_tool_with_endpoint, |
| 98 | ) |
| 99 | |
| 100 | if _runtime_mcp_endpoint: |
| 101 | return await execute_mcp_tool_with_endpoint( |
| 102 | _runtime_mcp_endpoint, name, params |
| 103 | ) |
| 104 | if os.environ.get("MCP_HTTP_ENDPOINT"): |
| 105 | return await execute_mcp_tool(name, params) |
| 106 | except asyncio.CancelledError: |
| 107 | raise |
| 108 | except Exception as e: |
| 109 | return json.dumps( |
| 110 | {"error": f"MCP execution failed: {e}"}, ensure_ascii=False |
| 111 | ) |
| 112 | return json.dumps( |
| 113 | {"error": f"Unknown tool: {name}", "arguments": params}, ensure_ascii=False |
| 114 | ) |
| 115 | |
| 116 | try: |
| 117 | result = func(params) |
| 118 | return json.dumps(result, ensure_ascii=False) |
| 119 | except Exception as e: |
| 120 | return json.dumps({"error": f"Execution failed: {e}"}, ensure_ascii=False) |