@@ -352,19 +352,20 @@ def _prepare_handler_data(
352352 )
353353
354354
355- async def _async_handler_wrapper (
355+ async def _handler_wrapper (
356356 handler_type : str ,
357357 func : "Callable[..., Any]" ,
358358 original_args : "tuple[Any, ...]" ,
359359 original_kwargs : "Optional[dict[str, Any]]" = None ,
360360 self : "Optional[Any]" = None ,
361+ force_await : bool = True ,
361362) -> "Any" :
362363 """
363- Async wrapper for MCP handlers.
364+ Wrapper for MCP handlers.
364365
365366 Args:
366367 handler_type: "tool", "prompt", or "resource"
367- func: The async handler function to wrap
368+ func: The handler function to wrap
368369 original_args: Original arguments passed to the handler
369370 original_kwargs: Original keyword arguments passed to the handler
370371 self: Optional instance for bound methods
@@ -421,73 +422,11 @@ async def _async_handler_wrapper(
421422 # Execute the async handler
422423 if self is not None :
423424 original_args = (self , * original_args )
424- result = await func (* original_args , ** original_kwargs )
425- except Exception as e :
426- # Set error flag for tools
427- if handler_type == "tool" :
428- span .set_data (SPANDATA .MCP_TOOL_RESULT_IS_ERROR , True )
429- sentry_sdk .capture_exception (e )
430- raise
431-
432- _set_span_output_data (span , result , result_data_key , handler_type )
433- return result
434-
435425
436- def _sync_handler_wrapper (
437- handler_type : str , func : "Callable[..., Any]" , original_args : "tuple[Any, ...]"
438- ) -> "Any" :
439- """
440- Sync wrapper for MCP handlers.
426+ result = func (* original_args , ** original_kwargs )
427+ if force_await or inspect .isawaitable (result ):
428+ result = await result
441429
442- Args:
443- handler_type: "tool", "prompt", or "resource"
444- func: The sync handler function to wrap
445- original_args: Original arguments passed to the handler
446- """
447- (
448- handler_name ,
449- arguments ,
450- span_data_key ,
451- span_name ,
452- mcp_method_name ,
453- result_data_key ,
454- ) = _prepare_handler_data (handler_type , original_args )
455-
456- # Start span and execute
457- with get_start_span_function ()(
458- op = OP .MCP_SERVER ,
459- name = span_name ,
460- origin = MCPIntegration .origin ,
461- ) as span :
462- # Get request ID, session ID, and transport from context
463- request_id , session_id , mcp_transport = _get_request_context_data ()
464-
465- # Set input span data
466- _set_span_input_data (
467- span ,
468- handler_name ,
469- span_data_key ,
470- mcp_method_name ,
471- arguments ,
472- request_id ,
473- session_id ,
474- mcp_transport ,
475- )
476-
477- # For resources, extract and set protocol
478- if handler_type == "resource" :
479- uri = original_args [0 ]
480- protocol = None
481- if hasattr (uri , "scheme" ):
482- protocol = uri .scheme
483- elif handler_name and "://" in handler_name :
484- protocol = handler_name .split ("://" )[0 ]
485- if protocol :
486- span .set_data (SPANDATA .MCP_RESOURCE_PROTOCOL , protocol )
487-
488- try :
489- # Execute the sync handler
490- result = func (* original_args )
491430 except Exception as e :
492431 # Set error flag for tools
493432 if handler_type == "tool" :
@@ -499,41 +438,6 @@ def _sync_handler_wrapper(
499438 return result
500439
501440
502- def _create_instrumented_handler (
503- handler_type : str , func : "Callable[..., Any]"
504- ) -> "Callable[..., Any]" :
505- """
506- Create an instrumented version of a handler function (async or sync).
507-
508- This function wraps the user's handler with a runtime wrapper that will create
509- Sentry spans and capture metrics when the handler is actually called.
510-
511- The wrapper preserves the async/sync nature of the original function, which is
512- critical for Python's async/await to work correctly.
513-
514- Args:
515- handler_type: "tool", "prompt", or "resource" - determines span configuration
516- func: The handler function to instrument (async or sync)
517-
518- Returns:
519- A wrapped version of func that creates Sentry spans on execution
520- """
521- if inspect .iscoroutinefunction (func ):
522-
523- @wraps (func )
524- async def async_wrapper (* args : "Any" ) -> "Any" :
525- return await _async_handler_wrapper (handler_type , func , args )
526-
527- return async_wrapper
528- else :
529-
530- @wraps (func )
531- def sync_wrapper (* args : "Any" ) -> "Any" :
532- return _sync_handler_wrapper (handler_type , func , args )
533-
534- return sync_wrapper
535-
536-
537441def _create_instrumented_decorator (
538442 original_decorator : "Callable[..., Any]" ,
539443 handler_type : str ,
@@ -547,8 +451,7 @@ def _create_instrumented_decorator(
547451 Sentry instrumentation into the handler registration flow. The returned decorator
548452 will:
549453 1. Receive the user's handler function
550- 2. Wrap it with instrumentation via _create_instrumented_handler
551- 3. Pass the instrumented version to the original MCP decorator
454+ 2. Pass the instrumented version to the original MCP decorator
552455
553456 This ensures that when the handler is called at runtime, it's already wrapped
554457 with Sentry spans and metrics collection.
@@ -564,12 +467,12 @@ def _create_instrumented_decorator(
564467 """
565468
566469 def instrumented_decorator (func : "Callable[..., Any]" ) -> "Callable[..., Any]" :
567- # First wrap the handler with instrumentation
568- instrumented_func = _create_instrumented_handler (handler_type , func )
470+ @wraps (func )
471+ async def wrapper (* args : "Any" ) -> "Any" :
472+ return await _handler_wrapper (handler_type , func , args , force_await = False )
473+
569474 # Then register it with the original MCP decorator
570- return original_decorator (* decorator_args , ** decorator_kwargs )(
571- instrumented_func
572- )
475+ return original_decorator (* decorator_args , ** decorator_kwargs )(wrapper )
573476
574477 return instrumented_decorator
575478
@@ -634,7 +537,7 @@ def _patch_fastmcp() -> None:
634537 async def patched_get_prompt_mcp (
635538 self : "Any" , * args : "Any" , ** kwargs : "Any"
636539 ) -> "Any" :
637- return await _async_handler_wrapper (
540+ return await _handler_wrapper (
638541 "prompt" ,
639542 original_get_prompt_mcp ,
640543 args ,
@@ -651,7 +554,7 @@ async def patched_get_prompt_mcp(
651554 async def patched_read_resource_mcp (
652555 self : "Any" , * args : "Any" , ** kwargs : "Any"
653556 ) -> "Any" :
654- return await _async_handler_wrapper (
557+ return await _handler_wrapper (
655558 "resource" ,
656559 original_read_resource_mcp ,
657560 args ,
0 commit comments