1
+ import functools
2
+ import logging
3
+ from typing import TypeVar , Callable
4
+ from mcp .types import TextContent
5
+ from fastmcp import FastMCP
6
+
7
+ T = TypeVar ('T' )
8
+
9
+ def handle_search_exceptions (func : Callable [..., T ]) -> Callable [..., list [TextContent ]]:
10
+ """
11
+ Decorator to handle exceptions in search client operations.
12
+
13
+ Args:
14
+ func: The function to decorate
15
+
16
+ Returns:
17
+ Decorated function that handles exceptions
18
+ """
19
+ @functools .wraps (func )
20
+ def wrapper (* args , ** kwargs ):
21
+ logger = logging .getLogger ()
22
+ try :
23
+ return func (* args , ** kwargs )
24
+ except Exception as e :
25
+ logger .error (f"Unexpected error in { func .__name__ } : { e } " )
26
+ return [TextContent (type = "text" , text = f"Unexpected error in { func .__name__ } : { str (e )} " )]
27
+
28
+ return wrapper
29
+
30
+ def with_exception_handling (tool_instance : object , mcp : FastMCP ) -> None :
31
+ """
32
+ Register tools from a tool instance with automatic exception handling applied to all tools.
33
+
34
+ This function temporarily replaces mcp.tool with a wrapped version that automatically
35
+ applies the handle_search_exceptions decorator to all registered tool methods.
36
+
37
+ Args:
38
+ tool_instance: The tool instance that has a register_tools method
39
+ mcp: The FastMCP instance used for tool registration
40
+ """
41
+ # Save the original tool method
42
+ original_tool = mcp .tool
43
+
44
+ @functools .wraps (original_tool )
45
+ def wrapped_tool (* args , ** kwargs ):
46
+ # Get the original decorator
47
+ decorator = original_tool (* args , ** kwargs )
48
+
49
+ # Return a new decorator that applies both the exception handler and original decorator
50
+ def combined_decorator (func ):
51
+ # First apply the exception handling decorator
52
+ wrapped_func = handle_search_exceptions (func )
53
+ # Then apply the original mcp.tool decorator
54
+ return decorator (wrapped_func )
55
+
56
+ return combined_decorator
57
+
58
+ try :
59
+ # Temporarily replace mcp.tool with our wrapped version
60
+ mcp .tool = wrapped_tool
61
+
62
+ # Call the registration method on the tool instance
63
+ tool_instance .register_tools (mcp )
64
+ finally :
65
+ # Restore the original mcp.tool to avoid affecting other code that might use mcp.tool
66
+ # This ensures that our modification is isolated to just this tool registration
67
+ # and prevents multiple nested decorators if register_all_tools is called multiple times
68
+ mcp .tool = original_tool
0 commit comments