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 pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-mcp"
version = "0.0.97"
version = "0.0.98"
description = "UiPath MCP SDK"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down
27 changes: 27 additions & 0 deletions src/uipath_mcp/_cli/_runtime/_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,33 @@ class UiPathMcpRuntimeContext(UiPathRuntimeContext):

config: Optional[McpConfig] = None
folder_key: Optional[str] = None
server_id: Optional[str] = None
server_slug: Optional[str] = None

@classmethod
def from_config(cls, config_path=None):
Copy link

Copilot AI Jul 21, 2025

Choose a reason for hiding this comment

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

The config_path parameter should have a type annotation. Consider adding config_path: Optional[str] = None for better API clarity.

Suggested change
def from_config(cls, config_path=None):
def from_config(cls, config_path: Optional[str] = None):

Copilot uses AI. Check for mistakes.
"""Load configuration from uipath.json file with MCP-specific handling."""
# Use the parent's implementation
instance = super().from_config(config_path)

# Convert to our type (since parent returns UiPathRuntimeContext)
mcp_instance = cls(**instance.model_dump())

# Add AgentHub-specific configuration handling
import json
Copy link

Copilot AI Jul 21, 2025

Choose a reason for hiding this comment

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

Imports should be placed at the top of the file rather than inside methods. Move the json and os imports to the module level.

Copilot uses AI. Check for mistakes.
import os

Comment on lines +27 to +29
Copy link

Copilot AI Jul 21, 2025

Choose a reason for hiding this comment

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

Imports should be placed at the top of the file rather than inside methods. Move the json and os imports to the module level.

Suggested change
import json
import os

Copilot uses AI. Check for mistakes.
path = config_path or "uipath.json"
if os.path.exists(path):
with open(path, "r") as f:
config = json.load(f)

if "fpsContext" in config:
fps_context = config["fpsContext"]
mcp_instance.server_id = fps_context.get("Id")
mcp_instance.server_slug = fps_context.get("Slug")

return mcp_instance


class UiPathServerType(Enum):
Expand Down
21 changes: 13 additions & 8 deletions src/uipath_mcp/_cli/_runtime/_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ async def execute(self) -> Optional[UiPathRuntimeResult]:

logger.info(f"Folder key: {self.context.folder_key}")

with tracer.start_as_current_span(self._server.name) as root_span:
with tracer.start_as_current_span(self.slug) as root_span:
root_span.set_attribute("runtime_id", self._runtime_id)
root_span.set_attribute("command", self._server.command)
root_span.set_attribute("args", self._server.args)
Expand Down Expand Up @@ -257,7 +257,7 @@ async def _handle_signalr_message(self, args: list) -> None:
# Check if we have a session server for this session_id
if session_id not in self._session_servers:
# Create and start a new session server
session_server = SessionServer(self._server, session_id)
session_server = SessionServer(self._server, self.slug, session_id)
try:
await session_server.start()
except Exception as e:
Expand Down Expand Up @@ -368,8 +368,9 @@ async def _register(self) -> None:
try:
client_info = {
"server": {
"Name": self._server.name,
"Slug": self._server.name,
"Id": self.context.server_id,
"Name": self.slug,
"Slug": self.slug,
"Version": "1.0.0",
"Type": self.server_type.value,
},
Expand All @@ -390,7 +391,7 @@ async def _register(self) -> None:
# Register with UiPath MCP Server
await self._uipath.api_client.request_async(
"POST",
f"agenthub_/mcp/{self._server.name}/runtime/start?runtimeId={self._runtime_id}",
f"agenthub_/mcp/{self.slug}/runtime/start?runtimeId={self._runtime_id}",
json=client_info,
headers={"X-UIPATH-FolderKey": self.context.folder_key},
)
Expand All @@ -417,14 +418,14 @@ async def _on_session_start_error(self, session_id: str) -> None:
try:
response = await self._uipath.api_client.request_async(
"POST",
f"agenthub_/mcp/{self._server.name}/out/message?sessionId={session_id}",
f"agenthub_/mcp/{self.slug}/out/message?sessionId={session_id}",
json=JSONRPCResponse(
jsonrpc="2.0",
id=0,
result={
"protocolVersion": "initialize-failure",
"capabilities": {},
"serverInfo": {"name": self._server.name, "version": "1.0"},
"serverInfo": {"name": self.slug, "version": "1.0"},
},
).model_dump(),
)
Expand Down Expand Up @@ -496,7 +497,7 @@ async def _on_runtime_abort(self) -> None:
try:
response = await self._uipath.api_client.request_async(
"POST",
f"agenthub_/mcp/{self._server.name}/runtime/abort?runtimeId={self._runtime_id}",
f"agenthub_/mcp/{self.slug}/runtime/abort?runtimeId={self._runtime_id}",
)
if response.status_code == 202:
logger.info(
Expand Down Expand Up @@ -536,6 +537,10 @@ def packaged(self) -> bool:
and process_key != "00000000-0000-0000-0000-000000000000"
)

@property
def slug(self) -> str:
return self.context.server_slug or self._server.name

@property
def server_type(self) -> UiPathServerType:
"""
Expand Down
11 changes: 6 additions & 5 deletions src/uipath_mcp/_cli/_runtime/_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@
class SessionServer:
"""Manages a server process for a specific session."""

def __init__(self, server_config: McpServer, session_id: str):
def __init__(self, server_config: McpServer, server_slug: str, session_id: str):
self._server_config = server_config
self._server_slug = server_slug
self._session_id = session_id
self._read_stream = None
self._write_stream = None
Expand Down Expand Up @@ -244,7 +245,7 @@ async def _send_message(self, message: JSONRPCMessage, request_id: str) -> None:
message,
session_id=self._session_id,
request_id=request_id,
server_name=self._server_config.name,
server_name=self._server_slug,
) as _:
for attempt in range(MAX_RETRIES + 1):
try:
Expand All @@ -268,7 +269,7 @@ async def _send_message_internal(
) -> None:
response = await self._uipath.api_client.request_async(
"POST",
f"agenthub_/mcp/{self._server_config.name}/out/message?sessionId={self._session_id}&requestId={request_id}",
f"agenthub_/mcp/{self._server_slug}/out/message?sessionId={self._session_id}&requestId={request_id}",
json=message.model_dump(),
)
if response.status_code == 202:
Expand All @@ -279,7 +280,7 @@ async def _send_message_internal(
async def _get_messages_internal(self, request_id: str) -> None:
response = await self._uipath.api_client.request_async(
"GET",
f"agenthub_/mcp/{self._server_config.name}/in/messages?sessionId={self._session_id}&requestId={request_id}",
f"agenthub_/mcp/{self._server_slug}/in/messages?sessionId={self._session_id}&requestId={request_id}",
)
if response.status_code == 200:
self._last_request_id = request_id
Expand All @@ -296,7 +297,7 @@ async def _get_messages_internal(self, request_id: str) -> None:
json_message,
session_id=self._session_id,
request_id=request_id,
server_name=self._server_config.name,
server_name=self._server_slug,
) as _:
await self._message_queue.put(json_message)
elif 500 <= response.status_code < 600:
Expand Down