diff --git a/navi/mcp/manager.py b/navi/mcp/manager.py index 65cff7f..60ba80f 100644 --- a/navi/mcp/manager.py +++ b/navi/mcp/manager.py @@ -35,6 +35,10 @@ self._health_check_task: asyncio.Task | None = None self._health_check_interval: float = 30.0 + # Last known connected status per server — used to suppress duplicate + # "connected" toast notifications on every health-check poll. + self._connected_status: dict[str, bool] = {} + @property def clients(self) -> dict[str, McpClient]: return self._clients @@ -84,12 +88,14 @@ try: await client.connect() self._clients[name] = client + self._connected_status[name] = True if self._on_server_connected: await self._on_server_connected(name) except Exception as exc: logger.warning("MCP server %r failed to connect: %s", name, exc) # Keep the client in the pool so health-check can retry later self._clients[name] = client + self._connected_status[name] = False async def reload_all(self) -> None: """Re-read the config file and reconnect every server.""" @@ -109,6 +115,7 @@ except Exception as exc: logger.warning("MCP server %r disconnect error: %s", name, exc) self._clients.clear() + self._connected_status.clear() async def get_all_tools(self) -> list[tuple[str, Any]]: """Return ``(server_name, mcp_tool)`` for every tool on every server. @@ -200,24 +207,31 @@ try: await client.connect() logger.info("MCP server %r reconnected by health check", name) + self._connected_status[name] = True if self._on_server_connected: await self._on_server_connected(name) except Exception as exc: logger.debug("MCP health-check reconnect failed for %r: %s", name, exc) continue - else: - # Live server — make sure it still responds - try: - await client.list_tools() - except Exception: - logger.warning("MCP server %r dropped during health check", name) - client.mark_disconnected() - await get_event_bus().publish( - McpStatusUpdate(server_name=name, status="disconnected") - ) - continue + continue - # If we get here the server is connected (either newly or verified) + # Live server — make sure it still responds + try: + await client.list_tools() + except Exception: + logger.warning("MCP server %r dropped during health check", name) + client.mark_disconnected() + self._connected_status[name] = False + await get_event_bus().publish( + McpStatusUpdate(server_name=name, status="disconnected") + ) + continue + + # Server is still connected — only notify if it was previously + # known as disconnected (recovery), not on every routine poll. + if self._connected_status.get(name): + continue + self._connected_status[name] = True try: tools = await client.list_tools() await get_event_bus().publish(