66import os
77import urllib .parse
88from collections .abc import AsyncGenerator , Awaitable , Callable
9- from dataclasses import dataclass , field
109from typing import Any
1110
1211import aiohttp
2322DEFAULT_ENABLE_MCP_TIMEOUT_SECONDS = 30.0
2423MCP_INIT_TIMEOUT_ENV = "ASTRBOT_MCP_INIT_TIMEOUT"
2524ENABLE_MCP_TIMEOUT_ENV = "ASTRBOT_MCP_ENABLE_TIMEOUT"
25+ MAX_MCP_TIMEOUT_SECONDS = 300.0
2626
2727
2828def _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
6461SUPPORTED_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