Skip to content

Commit 2c4b51a

Browse files
committed
Restore OTel span attribute helpers
1 parent b5b5389 commit 2c4b51a

File tree

3 files changed

+69
-29
lines changed

3 files changed

+69
-29
lines changed

src/mcp/server/lowlevel/server.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async def main():
6666
from mcp.server.streamable_http import EventStore
6767
from mcp.server.streamable_http_manager import StreamableHTTPASGIApp, StreamableHTTPSessionManager
6868
from mcp.server.transport_security import TransportSecuritySettings
69-
from mcp.shared._otel import extract_trace_context, otel_span
69+
from mcp.shared._otel import build_server_span_attributes, extract_trace_context, otel_span
7070
from mcp.shared._stream_protocols import ReadStream, WriteStream
7171
from mcp.shared.exceptions import MCPError
7272
from mcp.shared.message import ServerMessageMetadata, SessionMessage
@@ -455,32 +455,28 @@ async def _handle_request(
455455
# Extract W3C trace context from _meta (SEP-414).
456456
meta = cast(dict[str, Any] | None, getattr(req.params, "meta", None)) if req.params else None
457457
parent_context = extract_trace_context(meta) if meta is not None else None
458-
request_data = None
458+
server_message_metadata = (
459+
message.message_metadata if isinstance(message.message_metadata, ServerMessageMetadata) else None
460+
)
461+
request_data = server_message_metadata.request_context if server_message_metadata is not None else None
459462
close_sse_stream_cb = None
460463
close_standalone_sse_stream_cb = None
461-
if message.message_metadata is not None and isinstance(message.message_metadata, ServerMessageMetadata):
462-
request_data = message.message_metadata.request_context
463-
close_sse_stream_cb = message.message_metadata.close_sse_stream
464-
close_standalone_sse_stream_cb = message.message_metadata.close_standalone_sse_stream
464+
if server_message_metadata is not None:
465+
close_sse_stream_cb = server_message_metadata.close_sse_stream
466+
close_standalone_sse_stream_cb = server_message_metadata.close_standalone_sse_stream
465467
request_headers = getattr(request_data, "headers", None)
466468
session_id = request_headers.get(MCP_SESSION_ID_HEADER) if request_headers is not None else None
467-
span_attributes: dict[str, Any] = {
468-
"rpc.system": "mcp",
469-
"rpc.service": self.name,
470-
"rpc.method": req.method,
471-
"mcp.method.name": req.method,
472-
"jsonrpc.request.id": message.request_id,
473-
}
474-
resource_uri = getattr(req.params, "uri", None)
475-
if resource_uri is not None:
476-
span_attributes["mcp.resource.uri"] = str(resource_uri)
477-
if session_id is not None:
478-
span_attributes["mcp.session.id"] = session_id
479469

480470
with otel_span(
481471
span_name,
482472
kind=SpanKind.SERVER,
483-
attributes=span_attributes,
473+
attributes=build_server_span_attributes(
474+
service_name=self.name,
475+
method=req.method,
476+
request_id=message.request_id,
477+
params=req.params,
478+
session_id=session_id,
479+
),
484480
context=parent_context,
485481
) as span:
486482
if handler := self._request_handlers.get(req.method):

src/mcp/shared/_otel.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from opentelemetry.trace import SpanKind, get_tracer
1212

1313
_tracer = get_tracer("mcp-python-sdk")
14+
MCP_RPC_SYSTEM = "mcp"
1415

1516

1617
@contextmanager
@@ -34,3 +35,50 @@ def inject_trace_context(meta: dict[str, Any]) -> None:
3435
def extract_trace_context(meta: dict[str, Any]) -> Context:
3536
"""Extract W3C trace context from a `_meta` dict."""
3637
return extract(meta)
38+
39+
40+
def build_client_span_attributes(
41+
*,
42+
method: str,
43+
request_id: str | int,
44+
params: dict[str, Any] | None = None,
45+
) -> dict[str, Any]:
46+
"""Build OTel attributes for an MCP client request span."""
47+
attributes: dict[str, Any] = {
48+
"rpc.system": MCP_RPC_SYSTEM,
49+
"rpc.method": method,
50+
"mcp.method.name": method,
51+
"jsonrpc.request.id": request_id,
52+
}
53+
54+
if params is not None and (resource_uri := params.get("uri")) is not None:
55+
attributes["mcp.resource.uri"] = resource_uri
56+
57+
return attributes
58+
59+
60+
def build_server_span_attributes(
61+
*,
62+
service_name: str,
63+
method: str,
64+
request_id: str | int,
65+
params: Any = None,
66+
session_id: str | None = None,
67+
) -> dict[str, Any]:
68+
"""Build OTel attributes for an MCP server request span."""
69+
attributes: dict[str, Any] = {
70+
"rpc.system": MCP_RPC_SYSTEM,
71+
"rpc.service": service_name,
72+
"rpc.method": method,
73+
"mcp.method.name": method,
74+
"jsonrpc.request.id": request_id,
75+
}
76+
77+
resource_uri = getattr(params, "uri", None)
78+
if resource_uri is not None:
79+
attributes["mcp.resource.uri"] = str(resource_uri)
80+
81+
if session_id is not None:
82+
attributes["mcp.session.id"] = session_id
83+
84+
return attributes

src/mcp/shared/session.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from pydantic import BaseModel, TypeAdapter
1414
from typing_extensions import Self
1515

16-
from mcp.shared._otel import inject_trace_context, otel_span
16+
from mcp.shared._otel import build_client_span_attributes, inject_trace_context, otel_span
1717
from mcp.shared._stream_protocols import ReadStream, WriteStream
1818
from mcp.shared.exceptions import MCPError
1919
from mcp.shared.message import MessageMetadata, ServerMessageMetadata, SessionMessage
@@ -272,19 +272,15 @@ async def send_request(
272272
try:
273273
target = request_data.get("params", {}).get("name")
274274
span_name = f"MCP send {request.method} {target}" if target else f"MCP send {request.method}"
275-
span_attributes: dict[str, Any] = {
276-
"rpc.system": "mcp",
277-
"rpc.method": request.method,
278-
"mcp.method.name": request.method,
279-
"jsonrpc.request.id": request_id,
280-
}
281-
if (resource_uri := request_data.get("params", {}).get("uri")) is not None:
282-
span_attributes["mcp.resource.uri"] = resource_uri
283275

284276
with otel_span(
285277
span_name,
286278
kind=SpanKind.CLIENT,
287-
attributes=span_attributes,
279+
attributes=build_client_span_attributes(
280+
method=request.method,
281+
request_id=request_id,
282+
params=request_data.get("params"),
283+
),
288284
):
289285
# Inject W3C trace context into _meta (SEP-414).
290286
meta: dict[str, Any] = request_data.setdefault("params", {}).setdefault("_meta", {})

0 commit comments

Comments
 (0)