Skip to content

Commit 92d0572

Browse files
simplify
1 parent 41eece3 commit 92d0572

1 file changed

Lines changed: 33 additions & 78 deletions

File tree

sentry_sdk/integrations/mcp.py

Lines changed: 33 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
"""
99

1010
import inspect
11+
from collections.abc import Iterator
12+
from contextlib import contextmanager
1113
from functools import wraps
1214
from typing import TYPE_CHECKING
1315

@@ -53,7 +55,7 @@
5355

5456

5557
if TYPE_CHECKING:
56-
from typing import Any, Awaitable, Callable, ContextManager, Optional, Tuple, Union
58+
from typing import Any, Awaitable, Callable, Optional, Union
5759

5860
from mcp_types import (
5961
CallToolResult,
@@ -93,29 +95,48 @@ def setup_once() -> None:
9395
_patch_fastmcp()
9496

9597

96-
def _get_active_http_scopes(
97-
ctx: "Optional[Any]" = None,
98-
) -> "Optional[Tuple[Optional[sentry_sdk.Scope], Optional[sentry_sdk.Scope]]]":
98+
@contextmanager
99+
def _with_active_http_scopes(
100+
ctx: "Any" = None,
101+
) -> "Iterator[None]":
102+
"""
103+
Use isolation and current scopes that were stored before the in-memory MCP request queue.
104+
This ensures that MCP spans are nested under the HTTP server span when using the Streamable HTTP transport.
105+
"""
99106
if MCP_PACKAGE_VERSION and MCP_PACKAGE_VERSION < (2, 0, 0):
100107
if ctx is None:
101108
try:
102109
ctx = request_ctx.get()
103110
except LookupError:
104-
return None
111+
yield None
112+
return
105113

106114
if (
107115
ctx is None
108116
or not hasattr(ctx, "request")
109117
or ctx.request is None
110118
or "state" not in ctx.request.scope
111119
):
112-
return None
120+
yield
121+
return
113122

114-
return (
115-
ctx.request.scope["state"].get("sentry_sdk.isolation_scope"),
116-
ctx.request.scope["state"].get("sentry_sdk.current_scope"),
123+
isolation_scope = ctx.request.scope["state"].get("sentry_sdk.isolation_scope")
124+
current_scope = ctx.request.scope["state"].get("sentry_sdk.current_scope")
125+
126+
isolation_scope_context = (
127+
nullcontext()
128+
if isolation_scope is None
129+
else sentry_sdk.scope.use_isolation_scope(isolation_scope)
130+
)
131+
current_scope_context = (
132+
nullcontext()
133+
if current_scope is None
134+
else sentry_sdk.scope.use_scope(current_scope)
117135
)
118136

137+
with isolation_scope_context, current_scope_context:
138+
yield
139+
119140

120141
def _get_request_context_data(
121142
ctx: "Optional[Any]" = None,
@@ -541,35 +562,13 @@ async def _tool_handler_wrapper(
541562
result_data_key,
542563
) = _prepare_handler_data("tool", original_args, original_kwargs, params=params)
543564

544-
scopes = _get_active_http_scopes(ctx=ctx)
545-
546-
isolation_scope_context: "ContextManager[Any]"
547-
current_scope_context: "ContextManager[Any]"
548-
549-
if scopes is None:
550-
isolation_scope_context = nullcontext()
551-
current_scope_context = nullcontext()
552-
else:
553-
isolation_scope, current_scope = scopes
554-
555-
isolation_scope_context = (
556-
nullcontext()
557-
if isolation_scope is None
558-
else sentry_sdk.scope.use_isolation_scope(isolation_scope)
559-
)
560-
current_scope_context = (
561-
nullcontext()
562-
if current_scope is None
563-
else sentry_sdk.scope.use_scope(current_scope)
564-
)
565-
566565
# Get request ID, session ID, and transport from context
567566
request_id, session_id, mcp_transport = _get_request_context_data(ctx=ctx)
568567

569568
span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options)
570569

571570
# Start span and execute
572-
with isolation_scope_context, current_scope_context:
571+
with _with_active_http_scopes(ctx=ctx):
573572
span_mgr: "Union[Span, StreamedSpan]"
574573
if span_streaming:
575574
span_mgr = sentry_sdk.traces.start_span(
@@ -659,35 +658,13 @@ async def _prompt_handler_wrapper(
659658
result_data_key,
660659
) = _prepare_handler_data("prompt", original_args, original_kwargs, params=params)
661660

662-
scopes = _get_active_http_scopes(ctx=ctx)
663-
664-
isolation_scope_context: "ContextManager[Any]"
665-
current_scope_context: "ContextManager[Any]"
666-
667-
if scopes is None:
668-
isolation_scope_context = nullcontext()
669-
current_scope_context = nullcontext()
670-
else:
671-
isolation_scope, current_scope = scopes
672-
673-
isolation_scope_context = (
674-
nullcontext()
675-
if isolation_scope is None
676-
else sentry_sdk.scope.use_isolation_scope(isolation_scope)
677-
)
678-
current_scope_context = (
679-
nullcontext()
680-
if current_scope is None
681-
else sentry_sdk.scope.use_scope(current_scope)
682-
)
683-
684661
# Get request ID, session ID, and transport from context
685662
request_id, session_id, mcp_transport = _get_request_context_data(ctx=ctx)
686663

687664
span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options)
688665

689666
# Start span and execute
690-
with isolation_scope_context, current_scope_context:
667+
with _with_active_http_scopes(ctx=ctx):
691668
span_mgr: "Union[Span, StreamedSpan]"
692669
if span_streaming:
693670
span_mgr = sentry_sdk.traces.start_span(
@@ -775,35 +752,13 @@ async def _resource_handler_wrapper(
775752
result_data_key,
776753
) = _prepare_handler_data("resource", original_args, original_kwargs, params=params)
777754

778-
scopes = _get_active_http_scopes(ctx=ctx)
779-
780-
isolation_scope_context: "ContextManager[Any]"
781-
current_scope_context: "ContextManager[Any]"
782-
783-
if scopes is None:
784-
isolation_scope_context = nullcontext()
785-
current_scope_context = nullcontext()
786-
else:
787-
isolation_scope, current_scope = scopes
788-
789-
isolation_scope_context = (
790-
nullcontext()
791-
if isolation_scope is None
792-
else sentry_sdk.scope.use_isolation_scope(isolation_scope)
793-
)
794-
current_scope_context = (
795-
nullcontext()
796-
if current_scope is None
797-
else sentry_sdk.scope.use_scope(current_scope)
798-
)
799-
800755
# Get request ID, session ID, and transport from context
801756
request_id, session_id, mcp_transport = _get_request_context_data(ctx=ctx)
802757

803758
span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options)
804759

805760
# Start span and execute
806-
with isolation_scope_context, current_scope_context:
761+
with _with_active_http_scopes(ctx=ctx):
807762
span_mgr: "Union[Span, StreamedSpan]"
808763
if span_streaming:
809764
span_mgr = sentry_sdk.traces.start_span(

0 commit comments

Comments
 (0)