fix(MCP): 重构MCPClient为后台协程 + AsyncExitStack管理,解决进程退出时的“Attempted to exit cancel scope in a different task”错误

# 变更
----
- 将所有stdio_client与ClientSession的创建/销毁都放到同一个后台 task 中
- 使用AsyncExitStack托管异步资源,cleanup时在同一task内执行exit_stack.aclose()
- 外部只通过事件通知后台task退出,避免跨协程调用cancel-scope异常
This commit is contained in:
caixypromise
2025-05-04 23:27:47 +08:00
parent 9fc1285c09
commit 0a765f4aac
2 changed files with 137 additions and 77 deletions
+9 -8
View File
@@ -1,5 +1,5 @@
"""MCP服务管理器"""
import asyncio
import os, json
from typing import Dict, Any, List
from .MCPClient import MCPClient
@@ -94,8 +94,8 @@ class MCPManager:
"""
for tool in self.tools:
if (
tool.get("function") != None
and tool["function"].get("name") == tool_name
tool.get("function") != None
and tool["function"].get("name") == tool_name
):
return True
return False
@@ -120,12 +120,13 @@ class MCPManager:
raise ValueError(f"Tool {tool_name} not found in any MCP server")
async def cleanup_all(self) -> None:
for name, client in self.client.items():
"""依次关闭所有 MCPClient,不让异常阻断整体流程。"""
for name, client in list(self.client.items()):
try:
await client.cleanup()
self.logger.bind(tag=TAG).info(f"Cleaned up MCP client: {name}")
except Exception as e:
await asyncio.wait_for(client.cleanup(), timeout=20)
self.logger.bind(tag=TAG).info(f"MCP client closed: {name}")
except (asyncio.TimeoutError, Exception) as e:
self.logger.bind(tag=TAG).error(
f"Error cleaning up MCP client {name}: {e}"
f"Error closing MCP client {name}: {e}"
)
self.client.clear()