2121from sentry_sdk .traces import StreamedSpan
2222from sentry_sdk .tracing_utils import has_span_streaming_enabled
2323from sentry_sdk .utils import (
24+ capture_internal_exceptions ,
25+ event_from_exception ,
2426 has_data_collection_enabled ,
2527 nullcontext ,
2628 package_version ,
@@ -100,6 +102,15 @@ def setup_once() -> None:
100102 _patch_fastmcp ()
101103
102104
105+ def _capture_exception (exc : "Any" ) -> None :
106+ event , hint = event_from_exception (
107+ exc ,
108+ client_options = sentry_sdk .get_client ().options ,
109+ mechanism = {"type" : "mcp" , "handled" : False },
110+ )
111+ sentry_sdk .capture_event (event , hint = hint )
112+
113+
103114@contextmanager
104115def _active_http_scopes (
105116 ctx : "Any" ,
@@ -259,57 +270,6 @@ def _extract_text_from_content_blocks(content_blocks: "Any") -> "Any":
259270 return " " .join (texts ) if texts else content_blocks
260271
261272
262- def _extract_handler_data_from_args (
263- handler_type : str ,
264- original_args : "tuple[Any, ...]" ,
265- original_kwargs : "Optional[dict[str, Any]]" = None ,
266- ) -> "tuple[str, dict[str, Any]]" :
267- """
268- Extract handler name and arguments from v1 positional args.
269-
270- In MCP SDK v1, handlers receive positional args:
271- - Tool: (tool_name, arguments)
272- - Prompt: (name, arguments)
273- - Resource: (uri,)
274- """
275- original_kwargs = original_kwargs or {}
276-
277- if handler_type == "tool" :
278- if original_args :
279- handler_name = original_args [0 ]
280- elif original_kwargs .get ("name" ):
281- handler_name = original_kwargs ["name" ]
282-
283- arguments = {}
284- if len (original_args ) > 1 :
285- arguments = original_args [1 ]
286- elif original_kwargs .get ("arguments" ):
287- arguments = original_kwargs ["arguments" ]
288-
289- elif handler_type == "prompt" :
290- if original_args :
291- handler_name = original_args [0 ]
292- elif original_kwargs .get ("name" ):
293- handler_name = original_kwargs ["name" ]
294-
295- arguments = {}
296- if len (original_args ) > 1 :
297- arguments = original_args [1 ]
298- elif original_kwargs .get ("arguments" ):
299- arguments = original_kwargs ["arguments" ]
300-
301- else : # resource
302- handler_name = "unknown"
303- if original_args :
304- handler_name = str (original_args [0 ])
305- elif original_kwargs .get ("uri" ):
306- handler_name = str (original_kwargs ["uri" ])
307-
308- arguments = {}
309-
310- return handler_name , arguments
311-
312-
313273async def _tool_handler_wrapper (
314274 func : "Callable[..., Awaitable[Union[CallToolResult, InputRequiredResult]]]" ,
315275 original_args : "tuple[Any, ...]" ,
@@ -329,12 +289,18 @@ async def _tool_handler_wrapper(
329289 """
330290 client = sentry_sdk .get_client ()
331291
332- if original_kwargs is None :
333- original_kwargs = {}
292+ original_kwargs = original_kwargs or {}
334293
335- handler_name , arguments = _extract_handler_data_from_args (
336- "tool" , original_args , original_kwargs
337- )
294+ if original_args :
295+ handler_name = original_args [0 ]
296+ elif original_kwargs .get ("name" ):
297+ handler_name = original_kwargs ["name" ]
298+
299+ arguments = {}
300+ if len (original_args ) > 1 and original_args [1 ] is not None :
301+ arguments = original_args [1 ]
302+ elif original_kwargs .get ("arguments" ):
303+ arguments = original_kwargs ["arguments" ]
338304
339305 if has_data_collection_enabled (client .options ):
340306 if not client .options ["data_collection" ]["gen_ai" ]["inputs" ]:
@@ -394,7 +360,8 @@ async def _tool_handler_wrapper(
394360 result = await result
395361
396362 except Exception as e :
397- sentry_sdk .capture_exception (e )
363+ with capture_internal_exceptions ():
364+ _capture_exception (e )
398365 raise
399366
400367 if result is None :
@@ -490,7 +457,8 @@ async def _instrument_v2_tool_call(
490457 result = await call_next (ctx )
491458
492459 except Exception as e :
493- sentry_sdk .capture_exception (e )
460+ with capture_internal_exceptions ():
461+ _capture_exception (e )
494462 raise
495463
496464 if not isinstance (result , dict ):
@@ -549,13 +517,20 @@ async def _prompt_handler_wrapper(
549517 original_kwargs: Original keyword arguments passed to the handler
550518 self: Optional instance for bound methods
551519 """
552- if original_kwargs is None :
553- original_kwargs = {}
554-
555520 client = sentry_sdk .get_client ()
556- handler_name , arguments = _extract_handler_data_from_args (
557- "prompt" , original_args , original_kwargs
558- )
521+
522+ original_kwargs = original_kwargs or {}
523+
524+ if original_args :
525+ handler_name = original_args [0 ]
526+ elif original_kwargs .get ("name" ):
527+ handler_name = original_kwargs ["name" ]
528+
529+ arguments = {}
530+ if len (original_args ) > 1 and original_args [1 ] is not None :
531+ arguments = original_args [1 ]
532+ elif original_kwargs .get ("arguments" ):
533+ arguments = original_kwargs ["arguments" ]
559534
560535 if has_data_collection_enabled (client .options ):
561536 if not client .options ["data_collection" ]["gen_ai" ]["inputs" ]:
@@ -615,7 +590,8 @@ async def _prompt_handler_wrapper(
615590 result = await result
616591
617592 except Exception as e :
618- sentry_sdk .capture_exception (e )
593+ with capture_internal_exceptions ():
594+ _capture_exception (e )
619595 raise
620596
621597 if result is None :
@@ -764,7 +740,8 @@ async def _instrument_v2_prompt_get(
764740 try :
765741 result = await call_next (ctx )
766742 except Exception as e :
767- sentry_sdk .capture_exception (e )
743+ with capture_internal_exceptions ():
744+ _capture_exception (e )
768745 raise
769746
770747 if not isinstance (result , dict ):
@@ -847,12 +824,15 @@ async def _resource_handler_wrapper(
847824 original_kwargs: Original keyword arguments passed to the handler
848825 self: Optional instance for bound methods
849826 """
850- if original_kwargs is None :
851- original_kwargs = {}
827+ original_kwargs = original_kwargs or {}
852828
853- handler_name , arguments = _extract_handler_data_from_args (
854- "resource" , original_args , original_kwargs
855- )
829+ handler_name = "unknown"
830+ if original_args :
831+ handler_name = str (original_args [0 ])
832+ elif original_kwargs .get ("uri" ):
833+ handler_name = str (original_kwargs ["uri" ])
834+
835+ arguments : "dict[str, Any]" = {}
856836
857837 ctx = None
858838 try :
@@ -919,7 +899,8 @@ async def _resource_handler_wrapper(
919899 result = await result
920900
921901 except Exception as e :
922- sentry_sdk .capture_exception (e )
902+ with capture_internal_exceptions ():
903+ _capture_exception (e )
923904 raise
924905
925906 return result
@@ -984,7 +965,8 @@ async def _instrument_v2_resource_read(
984965 result = await call_next (ctx )
985966
986967 except Exception as e :
987- sentry_sdk .capture_exception (e )
968+ with capture_internal_exceptions ():
969+ _capture_exception (e )
988970 raise
989971
990972 return result
0 commit comments