fix(runtime): avoid RuntimeError when calling get_tool_specs with MCP tools from running event loop
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Anfängerfreundlichkeit
- 74/100
Rechercherichtung
Beginne in runtime/node/agent/tool/tool_manager.py, wo die zwei asyncio.run()-Aufrufe in etwa den Zeilen 198-204 (der HTTP-MCP-Abruf) und Zeile 227 (der stdio-MCP-Abruf) stehen, die von get_tool_specs aus erreicht werden. Reproduziere das Problem mit dem minimalen Beispiel aus dem Issue, indem du get_tool_specs aus einer async-Funktion heraus aufrufst. Fertig ist es, wenn dieser Aufruf MCP-Tool-Spezifikationen zurückgibt, ohne RuntimeError, sowohl außerhalb als auch innerhalb einer laufenden Event-Loop.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Summary & Context
In runtime/node/agent/tool/tool_manager.py, ToolManager.get_tool_specs() provides synchronous tool specification discovery for agent nodes (runtime/node/executor/agent_executor.py:77).
When configuring MCP tools (mcp_remote or mcp_local), _build_mcp_remote_specs() and _build_mcp_local_specs() invoke asyncio.run() to execute coroutines synchronously:
# Lines 198-204:
tools = asyncio.run(
self._fetch_mcp_tools_http(
config.server,
headers=config.headers,
timeout=config.timeout,
)
)
# Line 227:
tools = asyncio.run(self._fetch_mcp_tools_stdio(config, launch_key))
Problem & Impact
When an agent node with MCP tools is executed inside an asynchronous environment—such as FastAPI routes (server/routes/websocket.py, server/routes/execute.py), an async workflow executor, or an async test—an asyncio event loop is already active in the current thread.
Calling asyncio.run() while an event loop is active immediately raises:
RuntimeError: asyncio.run() cannot be called from a running event loop
This crashes the node execution and prevents MCP tools from being fetched or registered.
Minimal Reproducible Example
import asyncio
from runtime.node.agent.tool.tool_manager import ToolManager
from entity.configs.node.tooling import McpRemoteConfig, ToolingConfig
tool_manager = ToolManager()
mcp_cfg = ToolingConfig(
type="mcp_remote",
config=McpRemoteConfig(server="http://localhost:8000/sse", path="node.tooling.mcp_remote"),
path="node.tooling"
)
# Inside any async function (e.g. FastAPI endpoint or async runner):
async def run_step():
# Calling get_tool_specs invokes asyncio.run() internally:
return tool_manager.get_tool_specs([mcp_cfg])
asyncio.run(run_step())
# Result: RuntimeError: asyncio.run() cannot be called from a running event loop
Proposed Fix
Implement a thread-safe coroutine runner helper that detects if an event loop is already active in the current thread. If an event loop is running, delegate the coroutine execution to a worker thread via concurrent.futures.ThreadPoolExecutor to avoid RuntimeError:
import concurrent.futures
def _run_sync(coro):
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = None
if loop is not None and loop.is_running():
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
return pool.submit(asyncio.run, coro).result()
return asyncio.run(coro)
And update lines 198 and 227 in tool_manager.py to use _run_sync(...).
Empirical Verification
Tested with local reproduction script:
- Gate 1 (Outside event loop): Tools retrieved synchronously with 100% success.
- Gate 2 (Inside running event loop): Successfully executes coroutine in worker thread and returns tool specs without raising
RuntimeError.
- Vorherrschende Sprache
- Python
- Sterne
- 34.7k
- Forks
- 4.3k
- Ø Merge
- 3 Std. 23 Min.
- Gemergte PRs (30 T.)
- 3
Entwicklungsumgebung
- Enthält ein Dockerfile oder eine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Kein Beitragsleitfaden
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus OpenBMB/ChatDev
-
FunctionManager reload can execute stale bytecode after same-size source editsEvtl. vergeben @jadelike-wine hat das vor 18 Tagen übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 76/100
Maintainer antworten meist innerhalb von 1 Tag
-
CodeReviewModification never shows the programmer the six review regulations, so fixes are made blind to the checklist they are re-reviewed againstEvtl. vergeben Ein verknüpfter Pull Request ist offen oder bereits gemergt. Offen
Schwierigkeit 1/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 85/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 4/5 1-2 Tage Anfängerfreundlichkeit 8/100
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in OpenBMB/ChatDev
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
huggingface/deep-rl-class#723 ·
-
[tool] bug: Qwen3 XML tool parser raises on `...` argument values (Ellipsis is not JSON serializable)Evtl. vergeben @super-yuki hat das heute übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 87/100
verl-project/verl#8559 ·
Maintainer antworten meist innerhalb von 1 Tag
-
bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 75/100
TencentCloud/octop-harness#228 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
google/osv-scalibr#2585 ·
Maintainer antworten meist innerhalb von 2 Tagen
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 85/100
NousResearch/hermes-agent#149323 ·
Maintainer antworten meist innerhalb von 1 Tag