mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-19 16:12:30 +00:00
fix: use sequential MCP server connections to prevent CPU spin
asyncio.create_task in connect_mcp_servers creates child tasks for each MCP server, but close_mcp calls stack.aclose() from the main task. anyio CancelScope requires enter/exit in the same task, so the cross-task exit raises RuntimeError which gets silently caught. The orphaned cancel scope keeps retrying via call_soon on every event loop tick, consuming 100% CPU. Fix: remove create_task/gather and connect servers sequentially in the caller task. MCP servers are typically 1-2, so parallel connection provides negligible benefit while introducing the cancel scope hazard. Closes #3638
This commit is contained in:
parent
99209a806d
commit
4fad19dc17
@ -436,8 +436,8 @@ async def connect_mcp_servers(
|
||||
"""Connect to configured MCP servers and register their tools, resources, prompts.
|
||||
|
||||
Returns a dict mapping server name -> its dedicated AsyncExitStack.
|
||||
Each server gets its own stack and runs in its own task to prevent
|
||||
cancel scope conflicts when multiple MCP servers are configured.
|
||||
Each server gets its own stack to prevent cancel scope conflicts
|
||||
when multiple MCP servers are configured.
|
||||
"""
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.sse import sse_client
|
||||
@ -612,19 +612,13 @@ async def connect_mcp_servers(
|
||||
|
||||
server_stacks: dict[str, AsyncExitStack] = {}
|
||||
|
||||
tasks: list[asyncio.Task] = []
|
||||
for name, cfg in mcp_servers.items():
|
||||
task = asyncio.create_task(connect_single_server(name, cfg))
|
||||
tasks.append(task)
|
||||
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
for i, result in enumerate(results):
|
||||
name = list(mcp_servers.keys())[i]
|
||||
if isinstance(result, BaseException):
|
||||
if not isinstance(result, asyncio.CancelledError):
|
||||
logger.error("MCP server '{}' connection task failed: {}", name, result)
|
||||
elif result is not None and result[1] is not None:
|
||||
try:
|
||||
result = await connect_single_server(name, cfg)
|
||||
except Exception as e:
|
||||
logger.error("MCP server '{}' connection failed: {}", name, e)
|
||||
continue
|
||||
if result is not None and result[1] is not None:
|
||||
server_stacks[result[0]] = result[1]
|
||||
|
||||
return server_stacks
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user