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>
118 lines
3.9 KiB
Python
118 lines
3.9 KiB
Python
"""MCP server status panel and maintenance."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.deps import require_admin, require_readonly
|
|
from app.database import get_db
|
|
from app.mcp_manager.commands import enqueue_mcp_command, enqueue_mcp_command_async
|
|
from app.mcp_manager.manager import MCP_STATUS_KEY
|
|
from app.models.enums import TaskStatus
|
|
from app.models.task import Task
|
|
from app.models.user import User
|
|
from app.schemas import McpCommandResult, McpDevelopRequest, McpDevelopResult, McpServerOut
|
|
from app.services.audit import record_audit
|
|
from app.services.events import get_redis, publish_event
|
|
|
|
router = APIRouter(prefix="/api/mcp", tags=["mcp"])
|
|
|
|
|
|
@router.get("/servers", response_model=list[McpServerOut])
|
|
async def list_mcp_servers(_: User = Depends(require_readonly)):
|
|
raw = await get_redis().get(MCP_STATUS_KEY)
|
|
if not raw:
|
|
return []
|
|
data = json.loads(raw)
|
|
return [McpServerOut(**s) for s in data]
|
|
|
|
|
|
@router.post("/reload-all", response_model=McpCommandResult)
|
|
async def reload_all_mcps(_: User = Depends(require_admin)):
|
|
result = await enqueue_mcp_command("reload_all")
|
|
if not result.get("ok"):
|
|
raise HTTPException(status_code=502, detail=result.get("error", "Reload failed"))
|
|
return McpCommandResult(**result)
|
|
|
|
|
|
@router.post("/servers/{name}/reload", response_model=McpCommandResult)
|
|
async def reload_mcp_server(name: str, _: User = Depends(require_admin)):
|
|
result = await enqueue_mcp_command("reload", server=name)
|
|
if not result.get("ok"):
|
|
raise HTTPException(status_code=502, detail=result.get("error", "Reload failed"))
|
|
return McpCommandResult(**result)
|
|
|
|
|
|
@router.post("/servers/{name}/upgrade", response_model=McpCommandResult)
|
|
async def upgrade_mcp_server(name: str, _: User = Depends(require_admin)):
|
|
"""Git pull (or re-sync local copy) and restart the MCP server."""
|
|
result = await enqueue_mcp_command("upgrade", server=name)
|
|
if not result.get("ok"):
|
|
raise HTTPException(status_code=502, detail=result.get("error", "Upgrade failed"))
|
|
return McpCommandResult(**result)
|
|
|
|
|
|
@router.post("/servers/{name}/develop", response_model=McpDevelopResult)
|
|
async def develop_mcp_server(
|
|
name: str,
|
|
body: McpDevelopRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(require_admin),
|
|
):
|
|
"""Research and patch an MCP server; progress is streamed on a monitor task."""
|
|
task_id = body.task_id
|
|
if task_id:
|
|
res = await db.execute(select(Task).where(Task.id == task_id))
|
|
task = res.scalar_one_or_none()
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
task.status = TaskStatus.running
|
|
task.error = None
|
|
await db.commit()
|
|
else:
|
|
task = Task(
|
|
title=f"MCP develop: {name}",
|
|
issue=body.issue.strip(),
|
|
details={"kind": "mcp_develop", "mcp_server": name, "tool": body.tool},
|
|
status=TaskStatus.running,
|
|
created_by_id=user.id,
|
|
)
|
|
db.add(task)
|
|
await db.commit()
|
|
await db.refresh(task)
|
|
task_id = task.id
|
|
|
|
await publish_event(
|
|
task_id,
|
|
"log",
|
|
f"MCP develop queued for {name}",
|
|
{"server": name, "kind": "mcp_develop"},
|
|
persist=True,
|
|
)
|
|
await enqueue_mcp_command_async(
|
|
"develop",
|
|
server=name,
|
|
issue=body.issue.strip(),
|
|
tool=body.tool,
|
|
task_id=task_id,
|
|
push=body.push,
|
|
title=f"MCP develop: {name}",
|
|
)
|
|
await record_audit(
|
|
db,
|
|
user,
|
|
"mcp.develop",
|
|
"mcp",
|
|
task_id,
|
|
{"server": name, "issue": body.issue[:500]},
|
|
)
|
|
|
|
return McpDevelopResult(
|
|
ok=True,
|
|
task_id=task_id,
|
|
summary=f"Development started — open task #{task_id} to watch live progress.",
|
|
)
|