Skip to content

Commit 32e434f

Browse files
authored
Merge pull request #38 from UiPath/fix/samples
fix: proper error handling server init
2 parents 715adde + 63adec1 commit 32e434f

File tree

2 files changed

+70
-28
lines changed

2 files changed

+70
-28
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-mcp"
3-
version = "0.0.34"
3+
version = "0.0.35"
44
description = "UiPath MCP SDK"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.10"

src/uipath_mcp/_cli/_runtime/_runtime.py

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import os
44
import sys
5+
import tempfile
56
from typing import Any, Dict, Optional
67

78
from 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"\nServer 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

Comments
 (0)