55fetching their tool definitions, and building MCI-compatible toolset schemas.
66"""
77
8- import asyncio
8+ import asyncio , concurrent . futures
99from datetime import UTC , datetime , timedelta
1010from typing import Any
1111
@@ -75,6 +75,18 @@ def _annotations_to_tags(mcp_annotations: Any) -> list[str]:
7575
7676 return tags
7777
78+ @staticmethod
79+ async def fetch_and_build_toolset_async (
80+ server_name : str ,
81+ server_config : StdioMCPServer | HttpMCPServer ,
82+ schema_version : str ,
83+ env_context : dict [str , Any ],
84+ template_engine : TemplateEngine ,
85+ ) -> ToolsetSchema :
86+ return await MCPIntegration ._async_fetch_and_build_toolset (
87+ server_name , server_config , schema_version , env_context , template_engine
88+ )
89+
7890 @staticmethod
7991 def fetch_and_build_toolset (
8092 server_name : str ,
@@ -84,30 +96,37 @@ def fetch_and_build_toolset(
8496 template_engine : TemplateEngine ,
8597 ) -> ToolsetSchema :
8698 """
87- Fetch tools from an MCP server and build a toolset schema.
88-
89- Args:
90- server_name: Name of the MCP server
91- server_config: MCP server configuration (STDIO or HTTP)
92- schema_version: Schema version to use for the toolset
93- env_context: Environment context for templating
94- template_engine: Template engine for processing placeholders
95-
96- Returns:
97- ToolsetSchema with tools from the MCP server and expiration date
99+ Sync convenience for callers.
98100
99- Raises:
100- MCPIntegrationError: If MCP server connection or tool fetching fails
101+ - If NO event loop is running, use asyncio.run(...) in this thread.
102+ - If a loop IS running (e.g., inside an async CLI), offload the async
103+ work to a separate thread that owns its own loop, and block until it finishes.
101104 """
102- # Run async operation in sync context
103- try :
104- result = asyncio .run (
105- MCPIntegration ._async_fetch_and_build_toolset (
106- server_name , server_config , schema_version , env_context , template_engine
107- )
105+ async def _coro ():
106+ return await MCPIntegration .fetch_and_build_toolset_async (
107+ server_name , server_config , schema_version , env_context , template_engine
108108 )
109- return result
109+
110+ try :
111+ # detect running loop
112+ try :
113+ asyncio .get_running_loop ()
114+ loop_running = True
115+ except RuntimeError :
116+ loop_running = False
117+
118+ if not loop_running :
119+ return asyncio .run (_coro ())
120+
121+ # Run in separate thread if a loop is active
122+ def _run_in_thread ():
123+ return asyncio .run (_coro ())
124+
125+ with concurrent .futures .ThreadPoolExecutor (max_workers = 1 ) as ex :
126+ return ex .submit (_run_in_thread ).result ()
127+
110128 except Exception as e :
129+ # <-- place your error handler HERE
111130 raise MCPIntegrationError (
112131 f"Failed to fetch from MCP server '{ server_name } ': { e } "
113132 ) from e
0 commit comments