Add mcp-gateway/gateway-proxy/dashboard_routes.py
This commit is contained in:
parent
7e6ecb2e2b
commit
ba138171d5
1 changed files with 159 additions and 0 deletions
159
mcp-gateway/gateway-proxy/dashboard_routes.py
Normal file
159
mcp-gateway/gateway-proxy/dashboard_routes.py
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
"""
|
||||||
|
Dashboard routes for MCP Gateway status monitoring
|
||||||
|
Exposes service health, tool counts, and response metrics
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import aiohttp
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
|
# Define backend services with their details
|
||||||
|
BACKEND_SERVICES = {
|
||||||
|
"erpnext": {
|
||||||
|
"url": "http://mcp-erpnext:32802/mcp",
|
||||||
|
"display_name": "ERPNext",
|
||||||
|
"description": "ERP System Integration"
|
||||||
|
},
|
||||||
|
"truenas": {
|
||||||
|
"url": "http://mcp-truenas:8100/mcp",
|
||||||
|
"display_name": "TrueNAS",
|
||||||
|
"description": "NAS Storage Management"
|
||||||
|
},
|
||||||
|
"homeassistant": {
|
||||||
|
"url": "http://mcp-homeassistant:8200/mcp",
|
||||||
|
"display_name": "Home Assistant",
|
||||||
|
"description": "Home Automation"
|
||||||
|
},
|
||||||
|
"wave": {
|
||||||
|
"url": "http://mcp-wave:8300/mcp",
|
||||||
|
"display_name": "Wave Finance",
|
||||||
|
"description": "Financial Management"
|
||||||
|
},
|
||||||
|
"linkedin": {
|
||||||
|
"url": "http://mcp-linkedin:3000/mcp",
|
||||||
|
"display_name": "LinkedIn",
|
||||||
|
"description": "LinkedIn Integration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVICE_CACHE = {}
|
||||||
|
CACHE_TTL = 30 # Cache for 30 seconds
|
||||||
|
|
||||||
|
|
||||||
|
async def get_service_status() -> Dict:
|
||||||
|
"""
|
||||||
|
Get status of all MCP services
|
||||||
|
Returns health, tool count, response time, and other metrics
|
||||||
|
"""
|
||||||
|
services = []
|
||||||
|
|
||||||
|
for service_key, service_config in BACKEND_SERVICES.items():
|
||||||
|
service_data = {
|
||||||
|
"name": service_config.get("display_name", service_key),
|
||||||
|
"key": service_key,
|
||||||
|
"url": service_config["url"],
|
||||||
|
"status": "unknown",
|
||||||
|
"toolCount": 0,
|
||||||
|
"responseTime": None,
|
||||||
|
"lastCheck": None,
|
||||||
|
"error": None
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
# Try to fetch tools list
|
||||||
|
tools_url = f"{service_config['url']}/tools"
|
||||||
|
|
||||||
|
async with session.get(tools_url, timeout=aiohttp.ClientTimeout(total=5)) as response:
|
||||||
|
elapsed = (time.time() - start_time) * 1000 # Convert to ms
|
||||||
|
service_data["responseTime"] = round(elapsed, 2)
|
||||||
|
|
||||||
|
if response.status == 200:
|
||||||
|
try:
|
||||||
|
data = await response.json()
|
||||||
|
tools = data.get("tools", [])
|
||||||
|
service_data["toolCount"] = len(tools)
|
||||||
|
service_data["status"] = "healthy"
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
service_data["status"] = "warning"
|
||||||
|
service_data["error"] = "Invalid JSON response"
|
||||||
|
elif response.status == 503:
|
||||||
|
service_data["status"] = "warning"
|
||||||
|
service_data["error"] = "Service unavailable"
|
||||||
|
else:
|
||||||
|
service_data["status"] = "unhealthy"
|
||||||
|
service_data["error"] = f"HTTP {response.status}"
|
||||||
|
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
service_data["status"] = "unhealthy"
|
||||||
|
service_data["error"] = "Connection timeout"
|
||||||
|
service_data["responseTime"] = 5000
|
||||||
|
except aiohttp.ClientConnectorError:
|
||||||
|
service_data["status"] = "unhealthy"
|
||||||
|
service_data["error"] = "Connection refused"
|
||||||
|
except Exception as e:
|
||||||
|
service_data["status"] = "unhealthy"
|
||||||
|
service_data["error"] = str(e)
|
||||||
|
|
||||||
|
service_data["lastCheck"] = datetime.utcnow().isoformat()
|
||||||
|
services.append(service_data)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"timestamp": datetime.utcnow().isoformat(),
|
||||||
|
"services": services,
|
||||||
|
"summary": {
|
||||||
|
"total": len(services),
|
||||||
|
"healthy": len([s for s in services if s["status"] == "healthy"]),
|
||||||
|
"warning": len([s for s in services if s["status"] == "warning"]),
|
||||||
|
"unhealthy": len([s for s in services if s["status"] == "unhealthy"]),
|
||||||
|
"totalTools": sum(s["toolCount"] for s in services)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def setup_dashboard_routes(app):
|
||||||
|
"""
|
||||||
|
Register dashboard routes with the FastAPI app
|
||||||
|
"""
|
||||||
|
from fastapi.responses import FileResponse, JSONResponse
|
||||||
|
from fastapi import APIRouter
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/dashboard")
|
||||||
|
async def get_dashboard():
|
||||||
|
"""Serve the dashboard HTML"""
|
||||||
|
try:
|
||||||
|
return FileResponse("dashboard/dashboard.html", media_type="text/html")
|
||||||
|
except FileNotFoundError:
|
||||||
|
return JSONResponse(
|
||||||
|
{"error": "Dashboard not found"},
|
||||||
|
status_code=404
|
||||||
|
)
|
||||||
|
|
||||||
|
@router.get("/mcp/status")
|
||||||
|
async def get_mcp_status():
|
||||||
|
"""Get status of all MCP services"""
|
||||||
|
status = await get_service_status()
|
||||||
|
return JSONResponse(status)
|
||||||
|
|
||||||
|
@router.get("/mcp/status/{service_name}")
|
||||||
|
async def get_service_status_by_name(service_name: str):
|
||||||
|
"""Get status of a specific service"""
|
||||||
|
status = await get_service_status()
|
||||||
|
|
||||||
|
for service in status["services"]:
|
||||||
|
if service["key"] == service_name or service["name"].lower() == service_name.lower():
|
||||||
|
return JSONResponse(service)
|
||||||
|
|
||||||
|
return JSONResponse(
|
||||||
|
{"error": f"Service '{service_name}' not found"},
|
||||||
|
status_code=404
|
||||||
|
)
|
||||||
|
|
||||||
|
return router
|
||||||
Loading…
Reference in a new issue