feat(gui): Integrate External MCPs into Operations Hub with status indicators
This commit is contained in:
@@ -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."""
|
||||
|
||||
51
src/gui_2.py
51
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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user