13
13
14
14
from mcp .server .fastmcp import FastMCP
15
15
16
- from .backend import create_adapter
16
+ from .backend import BackendAdapter , create_adapter
17
17
18
18
19
19
class MCPWrapper :
@@ -28,6 +28,7 @@ def __init__(self, config_path: str):
28
28
self .mcp = FastMCP (server_name )
29
29
30
30
# Check if backend configuration exists
31
+ self .adapter : BackendAdapter | None = None
31
32
if "backend" in self .config :
32
33
self .adapter = create_adapter (self .config ["backend" ])
33
34
else :
@@ -54,18 +55,19 @@ def create_tool_function(self, tool_config: dict[str, Any]) -> Callable:
54
55
tool_name = tool_config ["name" ]
55
56
parameters = tool_config .get ("parameters" , [])
56
57
57
- def tool_executor (** kwargs ) :
58
+ def tool_executor (** kwargs : Any ) -> Any :
58
59
"""Generic function to execute tools"""
59
60
if self .adapter :
60
61
# Use adapter to execute tool
61
- def run_async_in_thread ():
62
+ def run_async_in_thread () -> Any :
62
63
"""Run async code in new thread"""
63
64
loop = asyncio .new_event_loop ()
64
65
asyncio .set_event_loop (loop )
65
66
try :
66
- return loop .run_until_complete (
67
- self .adapter .execute_tool (tool_config , kwargs )
68
- )
67
+ if self .adapter is not None :
68
+ return loop .run_until_complete (
69
+ self .adapter .execute_tool (tool_config , kwargs )
70
+ )
69
71
finally :
70
72
loop .close ()
71
73
@@ -86,8 +88,7 @@ def run_async_in_thread():
86
88
# Check if we have args specified in the tool config
87
89
if not args_template :
88
90
return (
89
- "Error: No backend adapter configured and no command "
90
- "specified"
91
+ "Error: No backend adapter configured and no command specified"
91
92
)
92
93
93
94
cmd_args = []
@@ -136,15 +137,24 @@ def run_async_in_thread():
136
137
137
138
# Create new function signature
138
139
new_signature = inspect .Signature (sig_params )
139
- tool_executor .__signature__ = new_signature
140
+
141
+ # Dynamically assign the generated signature.
142
+ # Normally we would assign via tool_executor.__signature__ = new_signature.
143
+ # However, since __signature__ is not a known attribute of FunctionType,
144
+ # directly assigning it would cause mypy to report an "attribute not defined" error.
145
+ #
146
+ # Using tool_executor.__dict__["__signature__"] = new_signature
147
+ # bypasses the static type checker, as mypy does not analyze __dict__ content.
148
+ # This allows us to safely inject the runtime signature without triggering type errors.
149
+ tool_executor .__dict__ ["__signature__" ] = new_signature
140
150
else :
141
151
# Function with no parameters
142
152
tool_executor .__annotations__ = {"return" : str }
143
- tool_executor .__signature__ = inspect .Signature ([])
153
+ tool_executor .__dict__ [ " __signature__" ] = inspect .Signature ([])
144
154
145
155
return tool_executor
146
156
147
- def _register_tools (self ):
157
+ def _register_tools (self ) -> None :
148
158
"""Register all tools to MCP server"""
149
159
for tool in self .config .get ("tools" , []):
150
160
tool_name = tool ["name" ]
@@ -156,21 +166,21 @@ def _register_tools(self):
156
166
# Register to MCP
157
167
self .mcp .tool (name = tool_name , description = tool_description )(tool_func )
158
168
159
- async def start_backend (self ):
169
+ async def start_backend (self ) -> None :
160
170
"""Start backend service"""
161
171
if self .adapter :
162
172
await self .adapter .start ()
163
173
164
- async def stop_backend (self ):
174
+ async def stop_backend (self ) -> None :
165
175
"""Stop backend service"""
166
176
if self .adapter :
167
177
await self .adapter .stop ()
168
178
169
- def server (self ):
179
+ def server (self ) -> FastMCP :
170
180
"""Run MCP server"""
171
181
return self .mcp
172
182
173
- def run (self ):
183
+ def run (self ) -> None :
174
184
"""Start MCP server"""
175
185
# If adapter exists, start backend service first
176
186
if self .adapter :
0 commit comments