fix: resolve MCP tools race condition causing 'completion 无法解析' error

- Wait for MCP client initialization to complete before accepting requests
- Add Future-based synchronization in init_mcp_clients()
- Prevent tool_calls from being rejected due to empty func_list
- Improve error logging for MCP initialization failures

Fixes race condition where AI attempts to call MCP tools before they are
registered, resulting in 'API 返回的 completion 无法解析' exceptions.

The issue occurred because:
1. MCP clients were initialized asynchronously without waiting
2. System accepted user requests immediately after startup
3. AI received empty tool list and attempted to call non-existent tools
4. Tool matching failed, causing parsing errors

This fix ensures all MCP tools are loaded before the system processes
any requests that might use them.
This commit is contained in:
idiotsj
2026-02-27 20:05:13 +08:00
parent 42e84afd89
commit 0de7ae8481
2 changed files with 24 additions and 3 deletions
+22 -1
View File
@@ -212,15 +212,36 @@ class FunctionToolManager:
open(mcp_json_file, encoding="utf-8"),
)["mcpServers"]
# 收集所有初始化任务的 Future
init_futures: dict[str, asyncio.Future] = {}
for name in mcp_server_json_obj:
cfg = mcp_server_json_obj[name]
if cfg.get("active", True):
event = asyncio.Event()
ready_future = asyncio.Future()
init_futures[name] = ready_future
asyncio.create_task(
self._init_mcp_client_task_wrapper(name, cfg, event),
self._init_mcp_client_task_wrapper(name, cfg, event, ready_future),
)
self.mcp_client_event[name] = event
# 等待所有 MCP 客户端初始化完成(或失败)
if init_futures:
logger.info(f"等待 {len(init_futures)} 个 MCP 服务初始化...")
results = await asyncio.gather(
*init_futures.values(), return_exceptions=True
)
success_count = 0
for name, result in zip(init_futures.keys(), results):
if isinstance(result, Exception):
logger.error(f"MCP 服务 {name} 初始化失败: {result}")
else:
success_count += 1
logger.info(f"MCP 服务初始化完成: {success_count}/{len(init_futures)} 成功")
async def _init_mcp_client_task_wrapper(
self,
name: str,
+2 -2
View File
@@ -274,8 +274,8 @@ class ProviderManager:
if not self.curr_tts_provider_inst and self.tts_provider_insts:
self.curr_tts_provider_inst = self.tts_provider_insts[0]
# 初始化 MCP Client 连接
asyncio.create_task(self.llm_tools.init_mcp_clients(), name="init_mcp_clients")
# 初始化 MCP Client 连接(等待完成以确保工具可用)
await self.llm_tools.init_mcp_clients()
def dynamic_import_provider(self, type: str):
"""动态导入提供商适配器模块