1212from sentry_sdk .integrations .mcp import MCPIntegration
1313from sentry_sdk .utils import safe_serialize
1414
15+ from mcp .server .lowlevel import Server
16+ from mcp .server .lowlevel .server import request_ctx
17+
18+
1519if TYPE_CHECKING :
1620 from typing import Any , Callable
1721
1822
1923def _get_span_config (handler_type , handler_name ):
20- # type: (str, str) -> tuple[str, str, str, str ]
24+ # type: (str, str) -> tuple[str, str, str]
2125 """
2226 Get span configuration based on handler type.
2327
2428 Returns:
25- Tuple of (op, span_data_key, span_name, mcp_method_name)
29+ Tuple of (span_data_key, span_name, mcp_method_name)
2630 """
2731 if handler_type == "tool" :
28- op = OP .MCP_TOOL
2932 span_data_key = SPANDATA .MCP_TOOL_NAME
3033 mcp_method_name = "tools/call"
3134 elif handler_type == "prompt" :
32- op = OP .MCP_PROMPT
3335 span_data_key = SPANDATA .MCP_PROMPT_NAME
3436 mcp_method_name = "prompts/get"
3537 else : # resource
36- op = OP .MCP_RESOURCE
3738 span_data_key = SPANDATA .MCP_RESOURCE_URI
3839 mcp_method_name = "resources/read"
3940
4041 span_name = f"{ handler_type } { handler_name } "
41- return op , span_data_key , span_name , mcp_method_name
42+ return span_data_key , span_name , mcp_method_name
4243
4344
44- def _set_span_data (span , handler_name , span_data_key , mcp_method_name , kwargs ):
45- # type: (Any, str, str, str, dict[str, Any]) -> None
45+ def _set_span_data (
46+ span , handler_name , span_data_key , mcp_method_name , kwargs , request_id = None
47+ ):
48+ # type: (Any, str, str, str, dict[str, Any], str | None) -> None
4649 """Set common span data for MCP handlers."""
4750 # Set handler identifier
4851 span .set_data (span_data_key , handler_name )
4952 span .set_data (SPANDATA .MCP_METHOD_NAME , mcp_method_name )
5053
54+ # Set request_id if provided
55+ if request_id :
56+ span .set_data (SPANDATA .MCP_REQUEST_ID , request_id )
57+
5158 # Set request arguments (excluding common request context objects)
5259 for k , v in kwargs .items ():
5360 span .set_data (f"mcp.request.argument.{ k } " , safe_serialize (v ))
@@ -66,7 +73,7 @@ def wrap_handler(original_handler, handler_type, handler_name):
6673 Returns:
6774 Wrapped handler function
6875 """
69- op , span_data_key , span_name , mcp_method_name = _get_span_config (
76+ span_data_key , span_name , mcp_method_name = _get_span_config (
7077 handler_type , handler_name
7178 )
7279
@@ -76,12 +83,25 @@ def wrap_handler(original_handler, handler_type, handler_name):
7683 async def async_wrapper (* args , ** kwargs ):
7784 # type: (*Any, **Any) -> Any
7885 with get_start_span_function ()(
79- op = op ,
86+ op = OP . MCP_SERVER ,
8087 name = span_name ,
8188 origin = MCPIntegration .origin ,
8289 ) as span :
90+ # Extract request_id from RequestContext context variable
91+ request_id = None
92+ try :
93+ ctx = request_ctx .get ()
94+ request_id = ctx .request_id
95+ except LookupError :
96+ # Not in a request context, request_id will remain None
97+ pass
8398 _set_span_data (
84- span , handler_name , span_data_key , mcp_method_name , kwargs
99+ span ,
100+ handler_name ,
101+ span_data_key ,
102+ mcp_method_name ,
103+ kwargs ,
104+ request_id ,
85105 )
86106 try :
87107 return await original_handler (* args , ** kwargs )
@@ -96,12 +116,25 @@ async def async_wrapper(*args, **kwargs):
96116 def sync_wrapper (* args , ** kwargs ):
97117 # type: (*Any, **Any) -> Any
98118 with get_start_span_function ()(
99- op = op ,
119+ op = OP . MCP_SERVER ,
100120 name = span_name ,
101121 origin = MCPIntegration .origin ,
102122 ) as span :
123+ # Extract request_id from RequestContext context variable
124+ request_id = None
125+ try :
126+ ctx = request_ctx .get ()
127+ request_id = ctx .request_id
128+ except LookupError :
129+ # Not in a request context, request_id will remain None
130+ pass
103131 _set_span_data (
104- span , handler_name , span_data_key , mcp_method_name , kwargs
132+ span ,
133+ handler_name ,
134+ span_data_key ,
135+ mcp_method_name ,
136+ kwargs ,
137+ request_id ,
105138 )
106139 try :
107140 return original_handler (* args , ** kwargs )
@@ -117,7 +150,6 @@ def patch_lowlevel_server():
117150 """
118151 Patches the mcp.server.lowlevel.Server class to instrument handler execution.
119152 """
120- from mcp .server .lowlevel import Server
121153
122154 # Patch call_tool decorator
123155 original_call_tool = Server .call_tool
0 commit comments