Skip to content

Commit f59527a

Browse files
ref(mcp): Inline _set_span_output_data()
1 parent 6ddc211 commit f59527a

1 file changed

Lines changed: 96 additions & 100 deletions

File tree

sentry_sdk/integrations/mcp.py

Lines changed: 96 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -247,100 +247,6 @@ def _extract_text_from_content_blocks(content_blocks: "Any") -> "Any":
247247
return " ".join(texts) if texts else content_blocks
248248

249249

250-
def _set_span_output_data(
251-
span: "Union[StreamedSpan, Span]",
252-
result: "Any",
253-
result_data_key: "Optional[str]",
254-
handler_type: str,
255-
) -> None:
256-
"""Set output span data for MCP handlers."""
257-
if result is None:
258-
return
259-
260-
# Get integration to check PII settings
261-
integration = sentry_sdk.get_client().get_integration(MCPIntegration)
262-
if integration is None:
263-
return
264-
265-
# Check if we should include sensitive data
266-
should_include_data = should_send_default_pii() and integration.include_prompts
267-
268-
# For tools, extract the meaningful content
269-
if handler_type == "tool":
270-
extracted = _extract_tool_result_content(result)
271-
if (
272-
extracted is not None
273-
and should_include_data
274-
and result_data_key is not None
275-
):
276-
_set_span_data_attribute(span, result_data_key, safe_serialize(extracted))
277-
# Set content count if result is a dict
278-
if isinstance(extracted, dict):
279-
_set_span_data_attribute(
280-
span, SPANDATA.MCP_TOOL_RESULT_CONTENT_COUNT, len(extracted)
281-
)
282-
elif handler_type == "prompt":
283-
# For prompts, count messages and set role/content only for single-message prompts
284-
try:
285-
messages: "Optional[list[str]]" = None
286-
message_count = 0
287-
288-
# Check if result has messages attribute (GetPromptResult)
289-
if hasattr(result, "messages") and result.messages:
290-
messages = result.messages
291-
message_count = len(messages)
292-
# Also check if result is a dict with messages
293-
elif isinstance(result, dict) and result.get("messages"):
294-
messages = result["messages"]
295-
message_count = len(messages)
296-
297-
# Always set message count if we found messages
298-
if message_count > 0:
299-
_set_span_data_attribute(
300-
span, SPANDATA.MCP_PROMPT_RESULT_MESSAGE_COUNT, message_count
301-
)
302-
303-
# Only set role and content for single-message prompts if PII is allowed
304-
if message_count == 1 and should_include_data and messages:
305-
first_message = messages[0]
306-
# Extract role
307-
role = None
308-
if hasattr(first_message, "role"):
309-
role = first_message.role
310-
elif isinstance(first_message, dict) and "role" in first_message:
311-
role = first_message["role"]
312-
313-
if role:
314-
_set_span_data_attribute(
315-
span, SPANDATA.MCP_PROMPT_RESULT_MESSAGE_ROLE, role
316-
)
317-
318-
# Extract content text
319-
content_text = None
320-
if hasattr(first_message, "content"):
321-
msg_content = first_message.content
322-
# Content can be a TextContent object or similar
323-
if hasattr(msg_content, "text"):
324-
content_text = msg_content.text
325-
elif isinstance(msg_content, dict) and "text" in msg_content:
326-
content_text = msg_content["text"]
327-
elif isinstance(msg_content, str):
328-
content_text = msg_content
329-
elif isinstance(first_message, dict) and "content" in first_message:
330-
msg_content = first_message["content"]
331-
if isinstance(msg_content, dict) and "text" in msg_content:
332-
content_text = msg_content["text"]
333-
elif isinstance(msg_content, str):
334-
content_text = msg_content
335-
336-
if content_text and result_data_key is not None:
337-
_set_span_data_attribute(span, result_data_key, content_text)
338-
except Exception:
339-
# Silently ignore if we can't extract message info
340-
pass
341-
# Resources don't capture result content (result_data_key is None)
342-
343-
344250
# Handler data preparation and wrapping
345251

346252

@@ -543,10 +449,30 @@ async def _tool_handler_wrapper(
543449
sentry_sdk.capture_exception(e)
544450
raise
545451

546-
_set_span_output_data(
547-
span, result, SPANDATA.MCP_TOOL_RESULT_CONTENT, "tool"
452+
if result is None:
453+
return result
454+
455+
# Get integration to check PII settings
456+
integration = sentry_sdk.get_client().get_integration(MCPIntegration)
457+
if integration is None:
458+
return result
459+
460+
# Check if we should include sensitive data
461+
should_include_data = (
462+
should_send_default_pii() and integration.include_prompts
548463
)
549464

465+
extracted = _extract_tool_result_content(result)
466+
if extracted is not None and should_include_data:
467+
_set_span_data_attribute(
468+
span, SPANDATA.MCP_TOOL_RESULT_CONTENT, safe_serialize(extracted)
469+
)
470+
# Set content count if result is a dict
471+
if isinstance(extracted, dict):
472+
_set_span_data_attribute(
473+
span, SPANDATA.MCP_TOOL_RESULT_CONTENT_COUNT, len(extracted)
474+
)
475+
550476
return result
551477

552478

@@ -661,10 +587,82 @@ async def _prompt_handler_wrapper(
661587
sentry_sdk.capture_exception(e)
662588
raise
663589

664-
_set_span_output_data(
665-
span, result, SPANDATA.MCP_PROMPT_RESULT_MESSAGE_CONTENT, "prompt"
590+
if result is None:
591+
return result
592+
593+
# Get integration to check PII settings
594+
integration = sentry_sdk.get_client().get_integration(MCPIntegration)
595+
if integration is None:
596+
return result
597+
598+
# Check if we should include sensitive data
599+
should_include_data = (
600+
should_send_default_pii() and integration.include_prompts
666601
)
667602

603+
# For prompts, count messages and set role/content only for single-message prompts
604+
try:
605+
messages: "Optional[list[str]]" = None
606+
message_count = 0
607+
608+
# Check if result has messages attribute (GetPromptResult)
609+
if hasattr(result, "messages") and result.messages:
610+
messages = result.messages # type: ignore[assignment]
611+
message_count = len(messages) # type: ignore[arg-type]
612+
# Also check if result is a dict with messages
613+
elif isinstance(result, dict) and result.get("messages"):
614+
messages = result["messages"]
615+
message_count = len(messages)
616+
617+
# Always set message count if we found messages
618+
if message_count > 0:
619+
_set_span_data_attribute(
620+
span, SPANDATA.MCP_PROMPT_RESULT_MESSAGE_COUNT, message_count
621+
)
622+
623+
# Only set role and content for single-message prompts if PII is allowed
624+
if message_count == 1 and should_include_data and messages:
625+
first_message = messages[0]
626+
# Extract role
627+
role = None
628+
if hasattr(first_message, "role"):
629+
role = first_message.role
630+
elif isinstance(first_message, dict) and "role" in first_message:
631+
role = first_message["role"]
632+
633+
if role:
634+
_set_span_data_attribute(
635+
span, SPANDATA.MCP_PROMPT_RESULT_MESSAGE_ROLE, role
636+
)
637+
638+
# Extract content text
639+
content_text = None
640+
if hasattr(first_message, "content"):
641+
msg_content = first_message.content
642+
# Content can be a TextContent object or similar
643+
if hasattr(msg_content, "text"):
644+
content_text = msg_content.text
645+
elif isinstance(msg_content, dict) and "text" in msg_content:
646+
content_text = msg_content["text"]
647+
elif isinstance(msg_content, str):
648+
content_text = msg_content
649+
elif isinstance(first_message, dict) and "content" in first_message:
650+
msg_content = first_message["content"]
651+
if isinstance(msg_content, dict) and "text" in msg_content:
652+
content_text = msg_content["text"]
653+
elif isinstance(msg_content, str):
654+
content_text = msg_content
655+
656+
if content_text:
657+
_set_span_data_attribute(
658+
span,
659+
SPANDATA.MCP_PROMPT_RESULT_MESSAGE_CONTENT,
660+
content_text,
661+
)
662+
except Exception:
663+
# Silently ignore if we can't extract message info
664+
pass
665+
668666
return result
669667

670668

@@ -800,8 +798,6 @@ async def _resource_handler_wrapper(
800798
sentry_sdk.capture_exception(e)
801799
raise
802800

803-
_set_span_output_data(span, result, None, "resource")
804-
805801
return result
806802

807803

0 commit comments

Comments
 (0)