2020from sentry_sdk .scope import should_send_default_pii
2121from sentry_sdk .traces import StreamedSpan
2222from sentry_sdk .tracing_utils import has_span_streaming_enabled
23- from sentry_sdk .utils import nullcontext , package_version , safe_serialize
23+ from sentry_sdk .utils import (
24+ has_data_collection_enabled ,
25+ nullcontext ,
26+ package_version ,
27+ safe_serialize ,
28+ )
2429
2530try :
2631 from mcp .server .lowlevel import Server
@@ -322,13 +327,21 @@ async def _tool_handler_wrapper(
322327 original_kwargs: Original keyword arguments passed to the handler
323328 self: Optional instance for bound methods
324329 """
330+ client = sentry_sdk .get_client ()
331+
325332 if original_kwargs is None :
326333 original_kwargs = {}
327334
328335 handler_name , arguments = _extract_handler_data_from_args (
329336 "tool" , original_args , original_kwargs
330337 )
331338
339+ if has_data_collection_enabled (client .options ):
340+ if not client .options ["data_collection" ]["gen_ai" ]["inputs" ]:
341+ # Arguments can contain sensitive data and shouldn't be added to the span
342+ # if the user has opted out via this config.
343+ arguments = {}
344+
332345 ctx = None
333346 try :
334347 ctx = request_ctx .get ()
@@ -388,14 +401,17 @@ async def _tool_handler_wrapper(
388401 return result
389402
390403 # Get integration to check PII settings
391- integration = sentry_sdk . get_client () .get_integration (MCPIntegration )
404+ integration = client .get_integration (MCPIntegration )
392405 if integration is None :
393406 return result
394407
395408 # Check if we should include sensitive data
396- should_include_data = (
397- should_send_default_pii () and integration .include_prompts
398- )
409+ should_include_data = False
410+ if has_data_collection_enabled (client .options ):
411+ if client .options ["data_collection" ]["gen_ai" ]["outputs" ]:
412+ should_include_data = True
413+ elif should_send_default_pii () and integration .include_prompts :
414+ should_include_data = True
399415
400416 extracted = _extract_tool_result_content (result )
401417 if extracted is not None and should_include_data :
@@ -422,15 +438,22 @@ async def _instrument_v2_tool_call(
422438 if ctx .params is None or ctx .params .get ("name" ) is None :
423439 return await call_next (ctx )
424440
441+ client = sentry_sdk .get_client ()
425442 handler_name = ctx .params ["name" ]
426443 arguments = ctx .params .get ("arguments" )
427444 if arguments is None :
428445 arguments = {}
429446
447+ if has_data_collection_enabled (client .options ):
448+ if not client .options ["data_collection" ]["gen_ai" ]["inputs" ]:
449+ # Arguments can contain sensitive data and shouldn't be added to the span
450+ # if the user has opted out via this config.
451+ arguments = {}
452+
430453 # Get request ID, session ID, and transport from context
431454 request_id , session_id , mcp_transport = _get_request_context_data (ctx = ctx )
432455
433- span_streaming = has_span_streaming_enabled (sentry_sdk . get_client () .options )
456+ span_streaming = has_span_streaming_enabled (client .options )
434457
435458 # Start span and execute
436459 with _active_http_scopes (ctx = ctx ):
@@ -474,22 +497,25 @@ async def _instrument_v2_tool_call(
474497 return result
475498
476499 # Get integration to check PII settings
477- integration = sentry_sdk . get_client () .get_integration (MCPIntegration )
500+ integration = client .get_integration (MCPIntegration )
478501 if integration is None :
479502 return result
480503
481504 # Check if we should include sensitive data
482- should_include_data = (
483- should_send_default_pii () and integration .include_prompts
484- )
505+ should_include_result_data = False
506+ if has_data_collection_enabled (client .options ):
507+ if client .options ["data_collection" ]["gen_ai" ]["outputs" ]:
508+ should_include_result_data = True
509+ elif should_send_default_pii () and integration .include_prompts :
510+ should_include_result_data = True
485511
486512 result_content = result
487513 if "structuredContent" in result :
488514 result_content = result ["structuredContent" ]
489515 elif isinstance (result .get ("content" ), list ):
490516 result_content = _extract_text_from_content_blocks (result ["content" ])
491517
492- if result_content is not None and should_include_data :
518+ if result_content is not None and should_include_result_data :
493519 _set_span_data_attribute (
494520 span ,
495521 SPANDATA .MCP_TOOL_RESULT_CONTENT ,
@@ -526,10 +552,17 @@ async def _prompt_handler_wrapper(
526552 if original_kwargs is None :
527553 original_kwargs = {}
528554
555+ client = sentry_sdk .get_client ()
529556 handler_name , arguments = _extract_handler_data_from_args (
530557 "prompt" , original_args , original_kwargs
531558 )
532559
560+ if has_data_collection_enabled (client .options ):
561+ if not client .options ["data_collection" ]["gen_ai" ]["inputs" ]:
562+ # Arguments can contain sensitive data and shouldn't be added to the span
563+ # if the user has opted out via this config.
564+ arguments = {}
565+
533566 ctx = None
534567 try :
535568 ctx = request_ctx .get ()
@@ -539,7 +572,7 @@ async def _prompt_handler_wrapper(
539572 # Get request ID, session ID, and transport from context
540573 request_id , session_id , mcp_transport = _get_request_context_data (ctx = ctx )
541574
542- span_streaming = has_span_streaming_enabled (sentry_sdk . get_client () .options )
575+ span_streaming = has_span_streaming_enabled (client .options )
543576
544577 # Start span and execute
545578 with _active_http_scopes (ctx = ctx ):
@@ -589,14 +622,17 @@ async def _prompt_handler_wrapper(
589622 return result
590623
591624 # Get integration to check PII settings
592- integration = sentry_sdk . get_client () .get_integration (MCPIntegration )
625+ integration = client .get_integration (MCPIntegration )
593626 if integration is None :
594627 return result
595628
596629 # Check if we should include sensitive data
597- should_include_data = (
598- should_send_default_pii () and integration .include_prompts
599- )
630+ should_include_result_data = False
631+ if has_data_collection_enabled (client .options ):
632+ if client .options ["data_collection" ]["gen_ai" ]["inputs" ]:
633+ should_include_result_data = True
634+ elif should_send_default_pii () and integration .include_prompts :
635+ should_include_result_data = True
600636
601637 # For prompts, count messages and set role/content only for single-message prompts
602638 try :
@@ -619,7 +655,7 @@ async def _prompt_handler_wrapper(
619655 )
620656
621657 # Only set role and content for single-message prompts if PII is allowed
622- if message_count == 1 and should_include_data and messages :
658+ if message_count == 1 and should_include_result_data and messages :
623659 first_message = messages [0 ]
624660 # Extract role
625661 role = None
@@ -675,15 +711,24 @@ async def _instrument_v2_prompt_get(
675711 if ctx .params is None or ctx .params .get ("name" ) is None :
676712 return await call_next (ctx )
677713
714+ client = sentry_sdk .get_client ()
678715 handler_name = ctx .params ["name" ]
716+
679717 arguments = ctx .params .get ("arguments" )
718+
680719 if arguments is None :
681720 arguments = {}
682721
722+ if has_data_collection_enabled (client .options ):
723+ if not client .options ["data_collection" ]["gen_ai" ]["inputs" ]:
724+ # Arguments can contain sensitive data and shouldn't be added to the span
725+ # if the user has opted out via this config.
726+ arguments = {}
727+
683728 # Get request ID, session ID, and transport from context
684729 request_id , session_id , mcp_transport = _get_request_context_data (ctx = ctx )
685730
686- span_streaming = has_span_streaming_enabled (sentry_sdk . get_client () .options )
731+ span_streaming = has_span_streaming_enabled (client .options )
687732
688733 # Start span and execute
689734 with _active_http_scopes (ctx = ctx ):
@@ -726,14 +771,17 @@ async def _instrument_v2_prompt_get(
726771 return result
727772
728773 # Get integration to check PII settings
729- integration = sentry_sdk . get_client () .get_integration (MCPIntegration )
774+ integration = client .get_integration (MCPIntegration )
730775 if integration is None :
731776 return result
732777
733778 # Check if we should include sensitive data
734- should_include_data = (
735- should_send_default_pii () and integration .include_prompts
736- )
779+ should_include_result_data = False
780+ if has_data_collection_enabled (client .options ):
781+ if client .options ["data_collection" ]["gen_ai" ]["inputs" ]:
782+ should_include_result_data = True
783+ elif should_send_default_pii () and integration .include_prompts :
784+ should_include_result_data = True
737785
738786 # For prompts, count messages and set role/content only for single-message prompts
739787 try :
@@ -751,7 +799,7 @@ async def _instrument_v2_prompt_get(
751799 )
752800
753801 # Only set role and content for single-message prompts if PII is allowed
754- if message_count == 1 and should_include_data and messages :
802+ if message_count == 1 and should_include_result_data and messages :
755803 first_message = messages [0 ]
756804 # Extract role
757805 role = None
0 commit comments