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>
This commit is contained in:
46
backend/app/mcp_manager/recovery.py
Normal file
46
backend/app/mcp_manager/recovery.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""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}")
|
||||
Reference in New Issue
Block a user