1919from uipath .tracing import wait_for_tracers
2020
2121from .._utils ._config import McpServer
22- from ._context import UiPathMcpRuntimeContext
22+ from ._context import UiPathMcpRuntimeContext , UiPathServerType
2323from ._exception import UiPathMcpRuntimeError
2424from ._session import SessionServer
2525from ._stdio_client import stdio_client
@@ -37,7 +37,9 @@ def __init__(self, context: UiPathMcpRuntimeContext):
3737 super ().__init__ (context )
3838 self .context : UiPathMcpRuntimeContext = context
3939 self ._server : Optional [McpServer ] = None
40- self ._runtime_id = self .context .job_id if self .context .job_id else str (uuid .uuid4 ())
40+ self ._runtime_id = (
41+ self .context .job_id if self .context .job_id else str (uuid .uuid4 ())
42+ )
4143 self ._signalr_client : Optional [SignalRClient ] = None
4244 self ._session_servers : Dict [str , SessionServer ] = {}
4345 self ._session_output : Optional [str ] = None
@@ -152,9 +154,7 @@ async def cleanup(self) -> None:
152154 try :
153155 await session_server .stop ()
154156 except Exception as e :
155- logger .error (
156- f"Error cleaning up session server { session_id } : { str (e )} "
157- )
157+ logger .error (f"Error cleaning up session server { session_id } : { str (e )} " )
158158
159159 if self ._signalr_client and hasattr (self ._signalr_client , "_transport" ):
160160 transport = self ._signalr_client ._transport
@@ -265,7 +265,7 @@ async def _register(self) -> None:
265265
266266 # Start a temporary stdio client to get tools
267267 # Use a temporary file to capture stderr
268- with tempfile .TemporaryFile (mode = ' w+b' ) as stderr_temp :
268+ with tempfile .TemporaryFile (mode = " w+b" ) as stderr_temp :
269269 async with stdio_client (server_params , errlog = stderr_temp ) as (
270270 read ,
271271 write ,
@@ -285,7 +285,9 @@ async def _register(self) -> None:
285285 logger .error ("Initialization timed out" )
286286 # Capture stderr output here, after the timeout
287287 stderr_temp .seek (0 )
288- server_stderr_output = stderr_temp .read ().decode ('utf-8' , errors = 'replace' )
288+ server_stderr_output = stderr_temp .read ().decode (
289+ "utf-8" , errors = "replace"
290+ )
289291 # We'll handle this after exiting the context managers
290292 # We don't continue with registration here - we'll do it after the context managers
291293
@@ -314,14 +316,13 @@ async def _register(self) -> None:
314316 "Name" : self ._server .name ,
315317 "Slug" : self ._server .name ,
316318 "Version" : "1.0.0" ,
317- "Type" : 1 if self .sandboxed else 3 ,
319+ "Type" : self .server_type . value ,
318320 },
319321 "tools" : [],
320322 }
321323
322324 for tool in tools_result .tools :
323325 tool_info = {
324- "Type" : 1 ,
325326 "Name" : tool .name ,
326327 "ProcessType" : "Tool" ,
327328 "Description" : tool .description ,
@@ -347,7 +348,7 @@ async def _register(self) -> None:
347348 async def _on_session_start_error (self , session_id : str ) -> None :
348349 """
349350 Sends a dummy initialization failure message to abort the already connected client.
350- Sanboxed runtimes are triggered by new client connections.
351+ Sandboxed runtimes are triggered by new client connections.
351352 """
352353 try :
353354 response = await self ._uipath .api_client .request_async (
@@ -382,6 +383,7 @@ async def _keep_alive(self) -> None:
382383 """
383384 while not self ._cancel_event .is_set ():
384385 try :
386+
385387 async def on_keep_alive_response (response : CompletionMessage ) -> None :
386388 if response .error :
387389 logger .error (f"Error during keep-alive: { response .error } " )
@@ -391,27 +393,33 @@ async def on_keep_alive_response(response: CompletionMessage) -> None:
391393 # If there are no active sessions and this is a sandbox environment
392394 # We need to cancel the runtime
393395 # eg: when user kills the agent that triggered the runtime, before we subscribe to events
394- if not session_ids and self .sandboxed and not self ._cancel_event .is_set ():
395- logger .error ("No active sessions, cancelling sandboxed runtime..." )
396+ if (
397+ not session_ids
398+ and self .sandboxed
399+ and not self ._cancel_event .is_set ()
400+ ):
401+ logger .error (
402+ "No active sessions, cancelling sandboxed runtime..."
403+ )
396404 self ._cancel_event .set ()
405+
397406 await self ._signalr_client .send (
398407 method = "OnKeepAlive" ,
399408 arguments = [],
400- on_invocation = on_keep_alive_response
409+ on_invocation = on_keep_alive_response ,
401410 )
402411 except Exception as e :
403412 logger .error (f"Error during keep-alive: { e } " )
404413 await asyncio .sleep (60 )
405414
406-
407415 async def _on_runtime_abort (self ) -> None :
408416 """
409417 Sends a runtime abort signalr to terminate all connected sessions.
410418 """
411419 try :
412420 response = await self ._uipath .api_client .request_async (
413421 "POST" ,
414- f"mcp_/mcp/{ self ._server .name } /runtime/abort?runtimeId={ self ._runtime_id } "
422+ f"mcp_/mcp/{ self ._server .name } /runtime/abort?runtimeId={ self ._runtime_id } " ,
415423 )
416424 if response .status_code == 202 :
417425 logger .info (
@@ -435,3 +443,37 @@ def sandboxed(self) -> bool:
435443 bool: True if this is an sandboxed runtime (has a job_id), False otherwise.
436444 """
437445 return self .context .job_id is not None
446+
447+ @property
448+ def packaged (self ) -> bool :
449+ """
450+ Check if the runtime is packaged (PackageType.MCPServer).
451+
452+ Returns:
453+ bool: True if this is a packaged runtime (has a process), False otherwise.
454+ """
455+ process_key = self .context .trace_context .process_key
456+
457+ return (
458+ process_key is not None
459+ and process_key != "00000000-0000-0000-0000-000000000000"
460+ )
461+
462+ @property
463+ def server_type (self ) -> UiPathServerType :
464+ """
465+ Determine the correct UiPathServerType for this runtime.
466+
467+ Returns:
468+ UiPathServerType: The appropriate server type enum value based on the runtime configuration.
469+ """
470+ if self .packaged :
471+ # If it's a packaged runtime (has a process_key), it's a Local server
472+ # Packaged runtimes are also sandboxed
473+ return UiPathServerType .Local
474+ elif self .sandboxed :
475+ # If it's sandboxed but not packaged, it's an External server
476+ return UiPathServerType .External
477+ else :
478+ # If it's neither packaged nor sandboxed, it's a Hosted server
479+ return UiPathServerType .Hosted
0 commit comments