Files
Agentic-OS/backend/app/mcp_manager/recovery.py
nearxos 6185b9b85a Initial commit: Agentic OS troubleshooting platform
Self-hosted, Docker-based agentic troubleshooting platform: FastAPI backend +
LangGraph agent, Next.js UI, tiered LLM routing (local Ollama -> Gemini ->
DeepSeek -> OpenRouter), MCP server manager, encrypted device credentials,
RBAC, audit log, project-memory + Obsidian integrations, and editable
troubleshooting decision rules tuned for the GeneseasX vessel stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 22:11:07 +03:00

47 lines
1.7 KiB
Python

"""Auto-reload MCP servers when a tool call fails due to server unavailability."""
from __future__ import annotations
import logging
from collections.abc import Awaitable, Callable
logger = logging.getLogger(__name__)
EmitFn = Callable[[int, str, str, dict | None], Awaitable[None]]
async def call_tool_resilient(
manager,
server: str,
tool: str,
args: dict,
*,
task_id: int | None = None,
emit: EmitFn | None = None,
max_retries: int = 1,
) -> dict:
"""Call MCP tool; on server-unavailable errors, reload that server and retry once."""
last_exc: Exception | None = None
for attempt in range(max_retries + 1):
try:
return await manager.call_tool(server, tool, args)
except RuntimeError as exc:
last_exc = exc
msg = str(exc).lower()
recoverable = "not available" in msg or "not loaded" in msg
if not recoverable or attempt >= max_retries:
raise
logger.warning("MCP %s unavailable for %s — reloading (attempt %s)", server, tool, attempt + 1)
if task_id and emit:
await emit(
task_id,
"log",
f"MCP '{server}' unavailable — reloading server and retrying {tool}",
{"server": server, "tool": tool},
)
try:
await manager.reload_server(server)
except Exception as reload_exc: # noqa: BLE001
logger.warning("MCP reload %s failed: %s", server, reload_exc)
raise exc from reload_exc
raise last_exc or RuntimeError(f"MCP call failed: {server}/{tool}")