feat: surface MCP server status in System Status page
Add per-tool in-memory call counters to the MCP module and a GET /api/v1/status/mcp endpoint that joins them with the live tool registry and endpoint config, then render it as a new card on the System Status page with a one-click client-config copy button. - app/mcp/stats.py: lock-guarded MCPStatsTracker (the mcp SDK runs sync tool bodies via anyio.to_thread.run_sync, so this is genuinely multi-threaded, unlike the async REST routes) - app/mcp/server.py: instrument search_regulations, add get_mcp_status() - app/config/settings.py: optional MCP_PUBLIC_URL override, required because the Vite proxy and reverse proxies rewrite the Host header - StatusPage.tsx: MCP Server card, joins the existing parallel fetch Counters are process-local by design; token usage is already persisted by ModelUsageTracker since MCP calls route through ask(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -4,10 +4,11 @@ import asyncio
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, Request
|
||||
|
||||
from app.config.settings import settings
|
||||
from app.domain.retrieval import RetrievedChunk
|
||||
from app.mcp.server import get_mcp_status
|
||||
from app.services.llm.llm_factory import get_llm_client, get_llm_factory
|
||||
from app.shared.bootstrap import (
|
||||
get_bm25_retriever,
|
||||
@@ -280,3 +281,17 @@ async def ping_model_connections():
|
||||
]
|
||||
await asyncio.gather(*tasks, return_exceptions=True)
|
||||
return {"models": [_build_model_status(role) for role in _MODEL_ROLES]}
|
||||
|
||||
|
||||
@router.get("/mcp")
|
||||
async def get_mcp_server_status(request: Request):
|
||||
"""Return MCP endpoint config, advertised tools, and per-tool call counters.
|
||||
|
||||
This route is a thin HTTP adapter: everything MCP-specific is assembled by
|
||||
app.mcp.server.get_mcp_status(). The only thing decided here is the public
|
||||
URL, because only the HTTP layer knows how the client reached us.
|
||||
"""
|
||||
# request.base_url already carries scheme/host/port and a trailing slash;
|
||||
# strip it before appending so the result is ".../mcp/", not ".../mcp//".
|
||||
public_url = settings.mcp_public_url or f"{str(request.base_url).rstrip('/')}/mcp/"
|
||||
return await get_mcp_status(public_url)
|
||||
|
||||
Reference in New Issue
Block a user