Skip to content

Commit 55a64d6

Browse files
committed
refactor: simplify MCP init flow and cap timeout values
1 parent 072a75d commit 55a64d6

1 file changed

Lines changed: 31 additions & 41 deletions

File tree

astrbot/core/provider/func_tool_manager.py

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import os
77
import urllib.parse
88
from collections.abc import AsyncGenerator, Awaitable, Callable
9-
from dataclasses import dataclass, field
109
from typing import Any
1110

1211
import aiohttp
@@ -23,6 +22,7 @@
2322
DEFAULT_ENABLE_MCP_TIMEOUT_SECONDS = 30.0
2423
MCP_INIT_TIMEOUT_ENV = "ASTRBOT_MCP_INIT_TIMEOUT"
2524
ENABLE_MCP_TIMEOUT_ENV = "ASTRBOT_MCP_ENABLE_TIMEOUT"
25+
MAX_MCP_TIMEOUT_SECONDS = 300.0
2626

2727

2828
def _resolve_timeout(
@@ -48,17 +48,14 @@ def _resolve_timeout(
4848
)
4949
return default
5050

51-
return timeout_value
52-
53-
54-
@dataclass
55-
class _McpClientInfo:
56-
"""跟踪单个 MCP 客户端初始化状态的内部数据类。"""
51+
if timeout_value > MAX_MCP_TIMEOUT_SECONDS:
52+
logger.warning(
53+
f"超时配置 {env_name}={timeout_value:g} 过大,已限制为最大值 "
54+
f"{MAX_MCP_TIMEOUT_SECONDS:g} 秒,以避免长时间等待。"
55+
)
56+
return MAX_MCP_TIMEOUT_SECONDS
5757

58-
name: str
59-
cfg: dict
60-
shutdown_event: asyncio.Event = field(default_factory=asyncio.Event)
61-
task: asyncio.Task | None = None
58+
return timeout_value
6259

6360

6461
SUPPORTED_TYPES = [
@@ -287,51 +284,48 @@ async def init_mcp_clients(self) -> None:
287284
)
288285
timeout_display = f"{init_timeout:g}"
289286

290-
# 用 _McpClientInfo 跟踪每个客户端的事件和任务
291-
client_infos: dict[str, _McpClientInfo] = {}
292-
287+
active_configs: list[tuple[str, dict, asyncio.Event]] = []
293288
for name, cfg in mcp_server_json_obj.items():
294289
if cfg.get("active", True):
295-
info = _McpClientInfo(name=name, cfg=cfg)
296-
client_infos[name] = info
290+
shutdown_event = asyncio.Event()
291+
active_configs.append((name, cfg, shutdown_event))
297292

298-
if not client_infos:
293+
if not active_configs:
299294
return
300295

301-
logger.info(f"等待 {len(client_infos)} 个 MCP 服务初始化...")
296+
logger.info(f"等待 {len(active_configs)} 个 MCP 服务初始化...")
302297

303298
init_tasks = [
304299
asyncio.create_task(
305300
self._start_mcp_client_with_timeout(
306-
name=info.name,
307-
cfg=info.cfg,
308-
shutdown_event=info.shutdown_event,
301+
name=name,
302+
cfg=cfg,
303+
shutdown_event=shutdown_event,
309304
timeout=init_timeout,
310305
),
311-
name=f"mcp-init:{info.name}",
306+
name=f"mcp-init:{name}",
312307
)
313-
for info in client_infos.values()
308+
for (name, cfg, shutdown_event) in active_configs
314309
]
315310
results = await asyncio.gather(*init_tasks, return_exceptions=True)
316311

317312
success_count = 0
318313
failed_services: list[str] = []
319314

320-
for info, result in zip(client_infos.values(), results, strict=False):
315+
for (name, cfg, shutdown_event), result in zip(
316+
active_configs, results, strict=False
317+
):
321318
if isinstance(result, Exception):
322319
if isinstance(result, TimeoutError):
323-
logger.error(
324-
f"MCP 服务 {info.name} 初始化超时({timeout_display}秒)"
325-
)
320+
logger.error(f"MCP 服务 {name} 初始化超时({timeout_display}秒)")
326321
else:
327-
logger.error(f"MCP 服务 {info.name} 初始化失败: {result}")
328-
self._log_safe_mcp_debug_config(info.cfg)
329-
failed_services.append(info.name)
330-
self.mcp_client_event.pop(info.name, None)
322+
logger.error(f"MCP 服务 {name} 初始化失败: {result}")
323+
self._log_safe_mcp_debug_config(cfg)
324+
failed_services.append(name)
325+
self.mcp_client_event.pop(name, None)
331326
continue
332327

333-
info.task = result
334-
self.mcp_client_event[info.name] = info.shutdown_event
328+
self.mcp_client_event[name] = shutdown_event
335329
success_count += 1
336330

337331
if failed_services:
@@ -340,11 +334,7 @@ async def init_mcp_clients(self) -> None:
340334
f"请检查配置文件 mcp_server.json 和服务器可用性。"
341335
)
342336

343-
logger.info(f"MCP 服务初始化完成: {success_count}/{len(client_infos)} 成功")
344-
345-
async def _init_mcp_client_once(self, name: str, cfg: dict) -> None:
346-
"""仅执行一次 MCP 初始化流程,失败直接抛出异常。"""
347-
await self._init_mcp_client(name, cfg)
337+
logger.info(f"MCP 服务初始化完成: {success_count}/{len(active_configs)} 成功")
348338

349339
async def _run_mcp_client(
350340
self,
@@ -371,16 +361,16 @@ async def _start_mcp_client_with_timeout(
371361
"""启动 MCP 客户端:先初始化,成功后再启动长生命周期任务。"""
372362
try:
373363
await asyncio.wait_for(
374-
self._init_mcp_client_once(name, cfg),
364+
self._init_mcp_client(name, cfg),
375365
timeout=timeout,
376366
)
377367
except asyncio.TimeoutError:
378368
await self._terminate_mcp_client(name)
379369
raise TimeoutError(f"MCP 服务 {name} 初始化超时({timeout:g} 秒)")
380-
except Exception as e:
370+
except Exception:
381371
logger.error(f"初始化 MCP 客户端 {name} 失败", exc_info=True)
382372
await self._terminate_mcp_client(name)
383-
raise e
373+
raise
384374

385375
return asyncio.create_task(
386376
self._run_mcp_client(name, shutdown_event),

0 commit comments

Comments
 (0)