Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.10
3.11
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[project]
name = "uipath-mcp"
version = "0.0.92"
version = "0.0.93"
description = "UiPath MCP SDK"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.10"
requires-python = ">=3.11"
dependencies = [
"mcp==1.9.0",
"mcp==1.10.1",
"pysignalr==1.3.0",
"uipath>=2.0.71",
"uipath>=2.0.73",
]
classifiers = [
"Development Status :: 3 - Alpha",
Expand Down
42 changes: 9 additions & 33 deletions src/uipath_mcp/_cli/_runtime/_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import os
import sys
import tempfile
import traceback
import uuid
from typing import Any, Dict, Optional

from httpx import HTTPStatusError
from mcp import ClientSession, StdioServerParameters, stdio_client
from mcp.types import JSONRPCResponse
from opentelemetry import trace
Expand Down Expand Up @@ -329,35 +329,11 @@ async def _register(self) -> None:
# We'll handle this after exiting the context managers
# We don't continue with registration here - we'll do it after the context managers

except BaseException as e:
# In Python 3.10, ExceptionGroup is in the 'exceptiongroup' module
# and asyncio.TaskGroup wraps exceptions in ExceptionGroup
if hasattr(e, "__context__") and e.__context__ is not None:
logger.error("Sub-exception details:")
except* Exception as eg:
for e in eg.exceptions:
logger.error(
"".join(
traceback.format_exception(
type(e.__context__),
e.__context__,
e.__context__.__traceback__,
)
)
)
elif hasattr(e, "exceptions"): # For ExceptionGroup
for i, sub_exc in enumerate(e.exceptions):
logger.error(f"Sub-exception {i + 1}:")
logger.error(
"".join(
traceback.format_exception(
type(sub_exc), sub_exc, sub_exc.__traceback__
)
)
)
else:
# Log the full traceback of the exception itself
logger.error("Full traceback:")
logger.error(
"".join(traceback.format_exception(type(e), e, e.__traceback__))
f"Unexpected error: {e}",
exc_info=True,
)

# Now that we're outside the context managers, check if initialization succeeded
Expand Down Expand Up @@ -395,8 +371,6 @@ async def _register(self) -> None:
}
client_info["tools"].append(tool_info)

logger.info(client_info)

# Register with UiPath MCP Server
await self._uipath.api_client.request_async(
"POST",
Expand All @@ -407,8 +381,10 @@ async def _register(self) -> None:
logger.info("Registered MCP Server type successfully")
except Exception as e:
logger.error(f"Error during registration: {e}")
if e.status_code == 400:
logger.error(f"Error details: {e.response.text}")
if isinstance(e, HTTPStatusError):
logger.error(
f"HTTP error details: {e.response.text} status code: {e.response.status_code}"
)
Copy link

Copilot AI Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Removing this info-level log may reduce visibility into client configuration during troubleshooting; consider demoting it to logger.debug instead of dropping it entirely.

Copilot uses AI. Check for mistakes.

Comment on lines 382 to 388
Copy link

Copilot AI Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider catching HTTPStatusError explicitly before a general Exception to differentiate HTTP errors from other failures and avoid large isinstance checks in the generic handler.

Copilot uses AI. Check for mistakes.
raise UiPathMcpRuntimeError(
"REGISTRATION_ERROR",
Expand Down
15 changes: 8 additions & 7 deletions src/uipath_mcp/_cli/_runtime/_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,16 @@ async def _run_server(self, server_params: StdioServerParameters) -> None:
# Cancel the consumer when we exit the loop
consumer_task.cancel()
try:
await consumer_task
except asyncio.CancelledError:
await asyncio.wait_for(consumer_task, timeout=2.0)
except (asyncio.CancelledError, asyncio.TimeoutError):
pass
Comment on lines +147 to 148
Copy link

Copilot AI Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Swallowing TimeoutError silently may hide issues if the consumer task doesn’t shut down as expected; consider logging a warning or taking remedial action on timeout.

Suggested change
except (asyncio.CancelledError, asyncio.TimeoutError):
pass
except asyncio.CancelledError:
pass
except asyncio.TimeoutError:
logger.warning(
f"Timeout occurred while shutting down consumer task for session {self._session_id}. Task may not have terminated cleanly."
)

Copilot uses AI. Check for mistakes.

except Exception as e:
logger.error(
f"Error in server process for session {self._session_id}: {e}",
exc_info=True,
)
except* Exception as eg:
for e in eg.exceptions:
logger.error(
f"Unexpected error for session {self._session_id}: {e}",
exc_info=True,
)
finally:
stderr_temp.seek(0)
self._server_stderr_output = stderr_temp.read().decode(
Expand Down
Loading