Skip to content

Commit 1276865

Browse files
committed
fix: response as child spans
1 parent a841ad1 commit 1276865

File tree

3 files changed

+73
-46
lines changed

3 files changed

+73
-46
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.32"
3+
version = "0.0.33"
44
description = "UiPath MCP SDK"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.10"

src/uipath_mcp/_cli/_runtime/_session.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import asyncio
22
import logging
3-
import time
43

54
import mcp.types as types
65
from mcp import StdioServerParameters
@@ -182,17 +181,11 @@ async def send_outgoing_message(self, message: types.JSONRPCMessage) -> None:
182181
message, session_id=self.session_id, server_name=self.server_config.name
183182
) as span:
184183
try:
185-
start_time = time.time()
186184
response = self._uipath.api_client.request(
187185
"POST",
188186
f"mcp_/mcp/{self.server_config.name}/out/message?sessionId={self.session_id}",
189187
json=message.model_dump(),
190188
)
191-
192-
duration_ms = (time.time() - start_time) * 1000
193-
span.set_attribute("http_duration_ms", duration_ms)
194-
span.set_attribute("http_status_code", response.status_code)
195-
196189
if response.status_code == 202:
197190
logger.info(
198191
f"Outgoing message sent to UiPath MCP Server: {message}"

src/uipath_mcp/_cli/_runtime/_tracer.py

Lines changed: 72 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22
import logging
3-
from typing import Optional, TypeVar
3+
from typing import Dict, Optional, TypeVar
44

55
import mcp.types as types
66
from opentelemetry import trace
@@ -19,55 +19,89 @@ def __init__(
1919
):
2020
self.tracer = tracer or trace.get_tracer(__name__)
2121
self.logger = logger or logging.getLogger(__name__)
22+
# Dictionary to store active request spans
23+
self._active_request_spans: Dict[str, Span] = {}
2224

23-
def create_span_for_message(
24-
self, message: types.JSONRPCMessage, **context
25-
) -> Span:
25+
def create_span_for_message(self, message: types.JSONRPCMessage, **context) -> Span:
2626
"""Create and configure a span for a message.
2727
2828
Args:
2929
message: The JSON-RPC message
30-
span_name: The name to use for the span
3130
**context: Additional context attributes to add to the span
3231
3332
Returns:
3433
A configured OpenTelemetry span
3534
"""
3635
root_value = message.root
3736

38-
if isinstance(root_value, types.JSONRPCRequest):
39-
span = self.tracer.start_span(f"{root_value.method} [{root_value.id}]")
40-
span.set_attribute("type", "request")
41-
span.set_attribute("span_type", "request")
42-
span.set_attribute("id", str(root_value.id))
43-
span.set_attribute("method", root_value.method)
44-
self._add_request_attributes(span, root_value)
45-
46-
elif isinstance(root_value, types.JSONRPCNotification):
47-
span = self.tracer.start_span(root_value.method)
48-
span.set_attribute("type", "notification")
49-
span.set_attribute("span_type", "notification")
50-
span.set_attribute("method", root_value.method)
51-
self._add_notification_attributes(span, root_value)
52-
53-
elif isinstance(root_value, types.JSONRPCResponse):
54-
span = self.tracer.start_span(f"response [{root_value.id}]")
55-
span.set_attribute("type", "response")
56-
span.set_attribute("span_type", "response")
57-
span.set_attribute("id", str(root_value.id))
58-
self._add_response_attributes(span, root_value)
59-
60-
elif isinstance(root_value, types.JSONRPCError):
61-
span = self.tracer.start_span(f"error [{root_value.id}]")
62-
span.set_attribute("type", "error")
63-
span.set_attribute("span_type", "error")
64-
span.set_attribute("id", str(root_value.id))
65-
span.set_attribute("error_code", root_value.error.code)
66-
span.set_attribute("error_message", root_value.error.message)
37+
# Check if this is a response to a tracked request
38+
parent_span = None
39+
if (
40+
isinstance(root_value, types.JSONRPCResponse)
41+
or isinstance(root_value, types.JSONRPCError)
42+
) and hasattr(root_value, "id"):
43+
request_id = str(root_value.id)
44+
parent_span = self._active_request_spans.get(request_id)
45+
46+
# Create span with appropriate context
47+
if parent_span:
48+
# This is a response to a tracked request - create as child span
49+
span_context = trace.set_span_in_context(parent_span)
50+
51+
if isinstance(root_value, types.JSONRPCResponse):
52+
span = self.tracer.start_span("response", context=span_context)
53+
span.set_attribute("type", "response")
54+
span.set_attribute("span_type", "response")
55+
span.set_attribute("id", str(root_value.id))
56+
self._add_response_attributes(span, root_value)
57+
else: # JSONRPCError
58+
span = self.tracer.start_span("error", context=span_context)
59+
span.set_attribute("type", "error")
60+
span.set_attribute("span_type", "error")
61+
span.set_attribute("id", str(root_value.id))
62+
span.set_attribute("error_code", root_value.error.code)
63+
span.set_attribute("error_message", root_value.error.message)
64+
65+
# Remove the request from active tracking
66+
self._active_request_spans.pop(request_id, None)
6767
else:
68-
span = self.tracer.start_span("unknown")
69-
span.set_attribute("span_type", str(type(root_value).__name__))
70-
span.set_attribute("type", str(type(root_value).__name__))
68+
# Create standard span based on message type
69+
if isinstance(root_value, types.JSONRPCRequest):
70+
span = self.tracer.start_span(f"{root_value.method}")
71+
span.set_attribute("type", "request")
72+
span.set_attribute("span_type", "request")
73+
span.set_attribute("id", str(root_value.id))
74+
span.set_attribute("method", root_value.method)
75+
self._add_request_attributes(span, root_value)
76+
77+
# Store for future response correlation
78+
self._active_request_spans[str(root_value.id)] = span
79+
80+
elif isinstance(root_value, types.JSONRPCNotification):
81+
span = self.tracer.start_span(root_value.method)
82+
span.set_attribute("type", "notification")
83+
span.set_attribute("span_type", "notification")
84+
span.set_attribute("method", root_value.method)
85+
self._add_notification_attributes(span, root_value)
86+
87+
elif isinstance(root_value, types.JSONRPCResponse):
88+
span = self.tracer.start_span("response")
89+
span.set_attribute("type", "response")
90+
span.set_attribute("span_type", "response")
91+
span.set_attribute("id", str(root_value.id))
92+
self._add_response_attributes(span, root_value)
93+
94+
elif isinstance(root_value, types.JSONRPCError):
95+
span = self.tracer.start_span("error")
96+
span.set_attribute("type", "error")
97+
span.set_attribute("span_type", "error")
98+
span.set_attribute("id", str(root_value.id))
99+
span.set_attribute("error_code", root_value.error.code)
100+
span.set_attribute("error_message", root_value.error.message)
101+
else:
102+
span = self.tracer.start_span("unknown")
103+
span.set_attribute("span_type", str(type(root_value).__name__))
104+
span.set_attribute("type", str(type(root_value).__name__))
71105

72106
# Add context attributes
73107
for key, value in context.items():
@@ -88,7 +122,7 @@ def _add_request_attributes(
88122
if request.method == "tools/call" and isinstance(request.params, dict):
89123
if "name" in request.params:
90124
span.set_attribute("tool_name", request.params["name"])
91-
span.update_name(f"{request.method}/{request.params['name']} [{request.id}]")
125+
span.update_name(f"{request.method}/{request.params['name']}")
92126
if "arguments" in request.params and isinstance(
93127
request.params["arguments"], dict
94128
):

0 commit comments

Comments
 (0)