22import logging
33import os
44import sys
5+ import tempfile
56from typing import Any , Dict , Optional
67
78from mcp import ClientSession , StdioServerParameters
@@ -63,6 +64,7 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:
6364 root_span .set_attribute ("session_id" , self .server .session_id )
6465 root_span .set_attribute ("command" , self .server .command )
6566 root_span .set_attribute ("args" , self .server .args )
67+ root_span .set_attribute ("span_type" , "server" )
6668 self .signalr_client = SignalRClient (
6769 signalr_url ,
6870 headers = {
@@ -208,6 +210,7 @@ async def _register(self) -> None:
208210
209211 initialization_successful = False
210212 tools_result = None
213+ server_stderr_output = ""
211214
212215 try :
213216 # Create a temporary session to get tools
@@ -218,32 +221,55 @@ async def _register(self) -> None:
218221 )
219222
220223 # Start a temporary stdio client to get tools
221- async with stdio_client (server_params ) as (read , write ):
222- async with ClientSession (read , write ) as session :
223- logger .info ("Initializing client session..." )
224- # Try to initialize with timeout
225- try :
226- await asyncio .wait_for (
227- session .initialize (),
228- timeout = 30
229- )
230- initialization_successful = True
231- logger .info ("Initialization successful" )
232-
233- # Only proceed if initialization was successful
234- tools_result = await session .list_tools ()
235- logger .info (tools_result )
236- except asyncio .TimeoutError :
237- logger .error ("Initialization timed out" )
238- # We'll handle this after exiting the context managers
239-
240- # We don't continue with registration here - we'll do it after the context managers
224+ # Use a temporary file to capture stderr
225+ with tempfile .TemporaryFile (mode = "w+" ) as stderr_temp :
226+ async with stdio_client (server_params , errlog = stderr_temp ) as (
227+ read ,
228+ write ,
229+ ):
230+ async with ClientSession (read , write ) as session :
231+ logger .info ("Initializing client session..." )
232+ # Try to initialize with timeout
233+ try :
234+ await asyncio .wait_for (session .initialize (), timeout = 30 )
235+ initialization_successful = True
236+ logger .info ("Initialization successful" )
237+
238+ # Only proceed if initialization was successful
239+ tools_result = await session .list_tools ()
240+ logger .info (tools_result )
241+ except asyncio .TimeoutError :
242+ logger .error ("Initialization timed out" )
243+ # Capture stderr output here, after the timeout
244+ stderr_temp .seek (0 )
245+ server_stderr_output = stderr_temp .read ()
246+ # We'll handle this after exiting the context managers
247+
248+ # We don't continue with registration here - we'll do it after the context managers
241249
242250 except BaseException as e :
243- # Just log the exception during cleanup - it's expected
244- # except (ProcessLookupError, ExceptionGroup) works with 3.11+
245- if "ProcessLookupError" in str (e ) or "ExceptionGroup" in str (e ):
246- logger .info ("Process already terminated during cleanup - this is expected" )
251+ is_process_lookup = False
252+ is_exception_group_with_lookup = False
253+
254+ if isinstance (e , ProcessLookupError ):
255+ is_process_lookup = True
256+ else :
257+ try :
258+ from exceptiongroup import ExceptionGroup # Backport for < 3.11
259+
260+ if sys .version_info >= (3 , 11 ) and isinstance (e , ExceptionGroup ):
261+ for sub_e in e .exceptions :
262+ if isinstance (sub_e , ProcessLookupError ):
263+ is_exception_group_with_lookup = True
264+ break
265+ except ImportError :
266+ # ExceptionGroup is not available (Python < 3.11)
267+ pass
268+
269+ if is_process_lookup or is_exception_group_with_lookup :
270+ logger .info (
271+ "Process already terminated during cleanup - this is expected"
272+ )
247273 else :
248274 logger .error (f"Error during server initialization: { e } " )
249275 raise UiPathMcpRuntimeError (
@@ -253,12 +279,28 @@ async def _register(self) -> None:
253279 UiPathErrorCategory .DEPLOYMENT ,
254280 ) from e
255281
282+ if is_process_lookup or is_exception_group_with_lookup :
283+ logger .info (
284+ "Process already terminated during cleanup - this is expected"
285+ )
286+ else :
287+ logger .error (f"Error during server initialization: { e } " )
288+ raise UiPathMcpRuntimeError (
289+ "INITIALIZATION_ERROR" ,
290+ "Server initialization failed" ,
291+ str (e ),
292+ UiPathErrorCategory .DEPLOYMENT ,
293+ ) from e
294+
256295 # Now that we're outside the context managers, check if initialization succeeded
257296 if not initialization_successful :
297+ error_message = "The server process failed to initialize. Verify environment variables are set correctly."
298+ if server_stderr_output :
299+ error_message += f"\n Server error output:\n { server_stderr_output } "
258300 raise UiPathMcpRuntimeError (
259- "TIMEOUT_ERROR " ,
260- "Server initialization timed out " ,
261- "The server process failed to initialize. Verify environment variables are set correctly." ,
301+ "INITIALIZATION_ERROR " ,
302+ "Server initialization failed " ,
303+ error_message ,
262304 UiPathErrorCategory .DEPLOYMENT ,
263305 )
264306
0 commit comments