diff --git a/src/app_controller.py b/src/app_controller.py index 0201e11..3e5d05b 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -953,6 +953,15 @@ class AppController: self.ui_agent_tools = {t: agent_tools_cfg.get(t, True) for t in models.AGENT_TOOL_NAMES} label = self.project.get("project", {}).get("name", "") session_logger.open_session(label=label) + # Trigger auto-start of MCP servers + self.event_queue.put('refresh_external_mcps', None) + + async def refresh_external_mcps(self): + await mcp_client.get_external_mcp_manager().stop_all() + # Start servers with auto_start=True + for name, cfg in self.mcp_config.mcpServers.items(): + if cfg.auto_start: + await mcp_client.get_external_mcp_manager().add_server(cfg) def cb_load_prior_log(self, path: Optional[str] = None) -> None: root = hide_tk_root() @@ -1266,6 +1275,9 @@ class AppController: "action": "ticket_completed", "payload": payload }) + elif event_name == "refresh_external_mcps": + import asyncio + asyncio.run(self.refresh_external_mcps()) def _handle_request_event(self, event: events.UserRequestEvent) -> None: """Processes a UserRequestEvent by calling the AI client.""" diff --git a/src/gui_2.py b/src/gui_2.py index 9bfa892..37fb206 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -608,6 +608,9 @@ class App: if imgui.begin_tab_item("Usage Analytics")[0]: self._render_usage_analytics_panel() imgui.end_tab_item() + if imgui.begin_tab_item("External Tools")[0]: + self._render_external_tools_panel() + imgui.end_tab_item() imgui.end_tab_bar() imgui.end() @@ -2573,6 +2576,54 @@ def hello(): imgui.pop_style_color(2) if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_response_panel") + def _render_external_tools_panel(self) -> None: + if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_external_tools_panel") + if imgui.button("Refresh External MCPs"): + self.event_queue.put("refresh_external_mcps", None) + + imgui.separator() + + # Server status indicators + manager = mcp_client.get_external_mcp_manager() + statuses = manager.get_servers_status() + if statuses: + imgui.text("Servers:") + for sname, status in statuses.items(): + imgui.same_line() + # Green for running, Yellow for starting, Red for error, Gray for idle + col = (0.5, 0.5, 0.5, 1.0) + if status == 'running': + col = (0.0, 1.0, 0.0, 1.0) + elif status == 'starting': + col = (1.0, 1.0, 0.0, 1.0) + elif status == 'error': + col = (1.0, 0.0, 0.0, 1.0) + imgui.color_button(f"##status_{sname}", col) + imgui.same_line() + imgui.text(sname) + imgui.separator() + + tools = manager.get_all_tools() + if not tools: + imgui.text_disabled("No external tools found.") + else: + if imgui.begin_table("external_tools_table", 3, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable): + imgui.table_setup_column("Name") + imgui.table_setup_column("Server") + imgui.table_setup_column("Description") + imgui.table_headers_row() + + for tname, tinfo in tools.items(): + imgui.table_next_row() + imgui.table_next_column() + imgui.text(tname) + imgui.table_next_column() + imgui.text(tinfo.get('server', 'unknown')) + imgui.table_next_column() + imgui.text(tinfo.get('description', '')) + imgui.end_table() + if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_external_tools_panel") + def _render_comms_history_panel(self) -> None: if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_comms_history_panel") st_col = vec4(200, 220, 160) diff --git a/src/mcp_client.py b/src/mcp_client.py index 6be254a..3fa8fca 100644 --- a/src/mcp_client.py +++ b/src/mcp_client.py @@ -925,12 +925,14 @@ class StdioMCPServer: self.tools = {} self._id_counter = 0 self._pending_requests = {} + self.status = 'idle' def _get_id(self): self._id_counter += 1 return self._id_counter async def start(self): + self.status = 'starting' self.proc = await asyncio.create_subprocess_exec( self.config.command, *self.config.args, @@ -940,6 +942,7 @@ class StdioMCPServer: ) asyncio.create_task(self._read_stderr()) await self.list_tools() + self.status = 'running' async def stop(self): if self.proc: @@ -955,6 +958,7 @@ class StdioMCPServer: except Exception: pass self.proc = None + self.status = 'idle' async def _read_stderr(self): while self.proc and not self.proc.stdout.at_eof(): @@ -1015,9 +1019,12 @@ class ExternalMCPManager: all_tools = {} for sname, server in self.servers.items(): for tname, tool in server.tools.items(): - all_tools[tname] = {**tool, 'server': sname} + all_tools[tname] = {**tool, 'server': sname, 'server_status': server.status} return all_tools + def get_servers_status(self) -> dict[str, str]: + return {name: server.status for name, server in self.servers.items()} + async def async_dispatch(self, tool_name: str, tool_input: dict) -> str: for server in self.servers.values(): if tool_name in server.tools: