Skip to content

Commit 16c95fc

Browse files
CopilotMatanga1-2
andcommitted
Fix dynamic actions loading by implementing lazy async initialization
Co-authored-by: Matanga1-2 <[email protected]>
1 parent d257f8a commit 16c95fc

File tree

3 files changed

+44
-11
lines changed

3 files changed

+44
-11
lines changed

src/models/tools/tool_map.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,42 @@
1313
class ToolMap:
1414
port_client: PortClient
1515
tools: dict[str, Tool] = field(default_factory=dict)
16+
_dynamic_tools_loaded: bool = field(default=False, init=False)
1617

1718
def __post_init__(self):
1819
# Register static tools
1920
for tool in mcp_tools.__all__:
2021
module = mcp_tools.__dict__[tool]
2122
self.register_tool(module(self.port_client))
2223
logger.info(f"ToolMap initialized with {len(self.tools)} static tools")
23-
self._register_dynamic_action_tools()
24+
# Don't load dynamic tools here - they'll be loaded lazily when needed
25+
26+
async def ensure_dynamic_tools_loaded(self) -> None:
27+
"""Ensure dynamic action tools are loaded (async version)."""
28+
if self._dynamic_tools_loaded:
29+
return
30+
31+
try:
32+
logger.info("Loading dynamic action tools asynchronously...")
33+
dynamic_manager = DynamicActionToolsManager(self.port_client)
34+
dynamic_tools = await dynamic_manager.get_dynamic_action_tools()
35+
36+
for tool in dynamic_tools:
37+
self.register_tool(tool)
38+
39+
logger.info(f"Successfully registered {len(dynamic_tools)} dynamic action tools")
40+
if len(dynamic_tools) == 0:
41+
logger.warning("No dynamic action tools were registered - this may indicate a configuration or network issue")
42+
43+
self._dynamic_tools_loaded = True
44+
except Exception as e:
45+
logger.error(f"Failed to register dynamic action tools: {e}")
46+
logger.exception("Full traceback for dynamic action tools registration failure:")
47+
# Mark as loaded even if failed to avoid repeated attempts
48+
self._dynamic_tools_loaded = True
2449

2550
def _register_dynamic_action_tools(self) -> None:
26-
"""Register dynamic tools for each Port action."""
51+
"""Register dynamic tools for each Port action (legacy sync version)."""
2752
try:
2853
logger.info("Starting dynamic action tools registration...")
2954
dynamic_manager = DynamicActionToolsManager(self.port_client)

src/server.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,17 @@ def main():
2828
@mcp.call_tool()
2929
async def call_tool(tool_name: str, arguments: dict[str, Any]):
3030
tool_map = get_tool_map()
31+
# Ensure dynamic tools are loaded before calling
32+
await tool_map.ensure_dynamic_tools_loaded()
3133
tool = tool_map.get_tool(tool_name)
3234
logger.debug(f"Calling tool: {tool_name} with arguments: {arguments}")
3335
return await execute_tool(tool, arguments)
3436

3537
@mcp.list_tools()
3638
async def list_tools() -> list[types.Tool]:
3739
tool_map = get_tool_map()
40+
# Ensure dynamic tools are loaded before listing
41+
await tool_map.ensure_dynamic_tools_loaded()
3842
return tool_map.list_tools()
3943

4044
# Run the server

src/tools/action/dynamic_actions.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,17 +143,21 @@ async def get_dynamic_action_tools(self) -> list[Tool]:
143143
return tools
144144

145145
def get_dynamic_action_tools_sync(self) -> list[Tool]:
146-
"""Synchronous wrapper for getting dynamic action tools."""
146+
"""Synchronous wrapper for getting dynamic action tools (fallback only)."""
147147
try:
148-
# First try the simple approach
149-
return asyncio.run(self.get_dynamic_action_tools())
150-
except RuntimeError as e:
151-
if "asyncio.run() cannot be called from a running event loop" in str(e):
152-
logger.warning("Cannot call asyncio.run from running event loop, skipping dynamic actions")
153-
return []
154-
else:
155-
logger.error(f"Failed to run dynamic action tools sync: {e}")
148+
# Check if we're already in an event loop
149+
try:
150+
asyncio.get_running_loop()
151+
# We're in an event loop, this shouldn't happen with the new architecture
152+
logger.warning("get_dynamic_action_tools_sync called from async context - this indicates a design issue")
153+
logger.warning("Dynamic actions should be loaded via ensure_dynamic_tools_loaded() instead")
156154
return []
155+
except RuntimeError:
156+
# No event loop running, use asyncio.run
157+
logger.info("No event loop running, using asyncio.run for dynamic actions")
158+
return asyncio.run(self.get_dynamic_action_tools())
159+
157160
except Exception as e:
158161
logger.error(f"Failed to run dynamic action tools sync: {e}")
162+
logger.exception("Full traceback for dynamic action tools sync failure:")
159163
return []

0 commit comments

Comments
 (0)