Upload folder using huggingface_hub
Browse files- pyproject.toml +1 -1
- src/mcp-hub/api.py +92 -70
pyproject.toml
CHANGED
|
@@ -123,7 +123,7 @@ dependencies = [
|
|
| 123 |
# MACHINE LEARNING
|
| 124 |
# =======================
|
| 125 |
"scikit-learn>=1.7.2",
|
| 126 |
-
"huggingface_hub
|
| 127 |
"datasets>=4.4.1",
|
| 128 |
|
| 129 |
# =======================
|
|
|
|
| 123 |
# MACHINE LEARNING
|
| 124 |
# =======================
|
| 125 |
"scikit-learn>=1.7.2",
|
| 126 |
+
"huggingface_hub>=0.23.2",
|
| 127 |
"datasets>=4.4.1",
|
| 128 |
|
| 129 |
# =======================
|
src/mcp-hub/api.py
CHANGED
|
@@ -27,6 +27,13 @@ except ImportError:
|
|
| 27 |
|
| 28 |
from pydantic import BaseModel
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
class LogEvent(BaseModel):
|
| 31 |
server: str
|
| 32 |
tool: str
|
|
@@ -305,78 +312,93 @@ async def get_server_logs(server_id: str):
|
|
| 305 |
@app.get("/api/servers")
|
| 306 |
async def list_servers():
|
| 307 |
"""Returns MCP servers with real metrics and HF status."""
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
if (PROJECT_ROOT / "src").exists():
|
| 313 |
-
for d in (PROJECT_ROOT / "src").iterdir():
|
| 314 |
-
if d.is_dir() and d.name.startswith("mcp-") and d.name != "mcp-hub":
|
| 315 |
-
readme_path = d / "README.md"
|
| 316 |
-
description = "MCP HUB Node"
|
| 317 |
-
if readme_path.exists():
|
| 318 |
-
lines = readme_path.read_text().split("\n")
|
| 319 |
-
# Try to find the first non-header line
|
| 320 |
-
for line in lines:
|
| 321 |
-
clean = line.strip()
|
| 322 |
-
if clean and not clean.startswith("#") and not clean.startswith("-"):
|
| 323 |
-
description = clean
|
| 324 |
-
break
|
| 325 |
-
|
| 326 |
-
name = d.name.replace("-", " ").title()
|
| 327 |
-
# Apply strict capitalization
|
| 328 |
-
for word in ["Mcp", "Sre", "Rag", "Seo", "mcp", "sre", "rag", "seo"]:
|
| 329 |
-
name = name.replace(word, word.upper())
|
| 330 |
-
|
| 331 |
-
description = description.replace("mcp", "MCP").replace("Mcp", "MCP").replace("sre", "SRE").replace("Sre", "SRE").replace("rag", "RAG").replace("Rag", "RAG").replace("seo", "SEO").replace("Seo", "SEO")
|
| 332 |
-
|
| 333 |
-
discovered[d.name] = {
|
| 334 |
-
"id": d.name,
|
| 335 |
-
"name": name,
|
| 336 |
-
"description": description
|
| 337 |
-
}
|
| 338 |
-
|
| 339 |
-
# 2. Merge with Known Servers (ensures we don't miss anything in Docker)
|
| 340 |
-
all_servers_map = {s["id"]: s for s in KNOWN_SERVERS}
|
| 341 |
-
all_servers_map.update(discovered) # Discovered overrides known if collision
|
| 342 |
-
|
| 343 |
-
servers_to_check = list(all_servers_map.values())
|
| 344 |
-
|
| 345 |
-
# 3. Check status in parallel
|
| 346 |
-
status_tasks = [get_hf_status(s["id"]) for s in servers_to_check]
|
| 347 |
-
statuses = await asyncio.gather(*status_tasks)
|
| 348 |
-
|
| 349 |
-
results = []
|
| 350 |
-
for idx, s in enumerate(servers_to_check):
|
| 351 |
-
server_metrics = metrics.get(s["id"], {"hourly": 0, "weekly": 0, "monthly": 0})
|
| 352 |
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 |
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 380 |
|
| 381 |
@app.get("/api/usage")
|
| 382 |
async def get_usage_trends(range: str = "24h"):
|
|
|
|
| 27 |
|
| 28 |
from pydantic import BaseModel
|
| 29 |
|
| 30 |
+
# Optional: HF Hub for status checks
|
| 31 |
+
try:
|
| 32 |
+
from huggingface_hub import HfApi
|
| 33 |
+
hf_api = HfApi()
|
| 34 |
+
except ImportError:
|
| 35 |
+
hf_api = None
|
| 36 |
+
|
| 37 |
class LogEvent(BaseModel):
|
| 38 |
server: str
|
| 39 |
tool: str
|
|
|
|
| 312 |
@app.get("/api/servers")
|
| 313 |
async def list_servers():
|
| 314 |
"""Returns MCP servers with real metrics and HF status."""
|
| 315 |
+
try:
|
| 316 |
+
print("DEBUG: list_servers called")
|
| 317 |
+
metrics = get_metrics()
|
| 318 |
+
print(f"DEBUG: metrics retrieved (keys: {list(metrics.keys())})")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
+
# 1. Discover local servers in src/
|
| 321 |
+
discovered = {}
|
| 322 |
+
if (PROJECT_ROOT / "src").exists():
|
| 323 |
+
for d in (PROJECT_ROOT / "src").iterdir():
|
| 324 |
+
if d.is_dir() and d.name.startswith("mcp-") and d.name != "mcp-hub":
|
| 325 |
+
readme_path = d / "README.md"
|
| 326 |
+
description = "MCP HUB Node"
|
| 327 |
+
if readme_path.exists():
|
| 328 |
+
lines = readme_path.read_text().split("\n")
|
| 329 |
+
# Try to find the first non-header line
|
| 330 |
+
for line in lines:
|
| 331 |
+
clean = line.strip()
|
| 332 |
+
if clean and not clean.startswith("#") and not clean.startswith("-"):
|
| 333 |
+
description = clean
|
| 334 |
+
break
|
| 335 |
+
|
| 336 |
+
name = d.name.replace("-", " ").title()
|
| 337 |
+
# Apply strict capitalization
|
| 338 |
+
for word in ["Mcp", "Sre", "Rag", "Seo", "mcp", "sre", "rag", "seo"]:
|
| 339 |
+
name = name.replace(word, word.upper())
|
| 340 |
+
|
| 341 |
+
description = description.replace("mcp", "MCP").replace("Mcp", "MCP").replace("sre", "SRE").replace("Sre", "SRE").replace("rag", "RAG").replace("Rag", "RAG").replace("seo", "SEO").replace("Seo", "SEO")
|
| 342 |
+
|
| 343 |
+
discovered[d.name] = {
|
| 344 |
+
"id": d.name,
|
| 345 |
+
"name": name,
|
| 346 |
+
"description": description
|
| 347 |
+
}
|
| 348 |
+
print(f"DEBUG: Discovered {len(discovered)} local servers")
|
| 349 |
|
| 350 |
+
# 2. Merge with Known Servers (ensures we don't miss anything in Docker)
|
| 351 |
+
all_servers_map = {s["id"]: s for s in KNOWN_SERVERS}
|
| 352 |
+
all_servers_map.update(discovered) # Discovered overrides known if collision
|
| 353 |
+
|
| 354 |
+
servers_to_check = list(all_servers_map.values())
|
| 355 |
+
print(f"DEBUG: Checking status for {len(servers_to_check)} servers")
|
| 356 |
+
|
| 357 |
+
# 3. Check status in parallel
|
| 358 |
+
# Wrap status check to ensure it doesn't fail
|
| 359 |
+
try:
|
| 360 |
+
status_tasks = [get_hf_status(s["id"]) for s in servers_to_check]
|
| 361 |
+
statuses = await asyncio.gather(*status_tasks)
|
| 362 |
+
except Exception as e:
|
| 363 |
+
print(f"DEBUG: Status check failed: {e}")
|
| 364 |
+
statuses = ["Unknown"] * len(servers_to_check)
|
| 365 |
+
|
| 366 |
+
results = []
|
| 367 |
+
for idx, s in enumerate(servers_to_check):
|
| 368 |
+
server_metrics = metrics.get(s["id"], {"hourly": 0, "weekly": 0, "monthly": 0})
|
| 369 |
+
|
| 370 |
+
def fmt(n):
|
| 371 |
+
if n is None: return "0"
|
| 372 |
+
if n >= 1000: return f"{n/1000:.1f}k"
|
| 373 |
+
return str(n)
|
| 374 |
+
|
| 375 |
+
name = s["name"]
|
| 376 |
+
for word in ["Mcp", "Sre", "Rag", "Seo", "mcp", "sre", "rag", "seo"]:
|
| 377 |
+
name = name.replace(word, word.upper())
|
| 378 |
+
|
| 379 |
+
results.append({
|
| 380 |
+
**s,
|
| 381 |
+
"name": name,
|
| 382 |
+
"status": statuses[idx],
|
| 383 |
+
"metrics": {
|
| 384 |
+
"hourly": fmt(server_metrics.get("hourly", 0)),
|
| 385 |
+
"weekly": fmt(server_metrics.get("weekly", 0)),
|
| 386 |
+
"monthly": fmt(server_metrics.get("monthly", 0)),
|
| 387 |
+
"raw_hourly": server_metrics.get("hourly", 0),
|
| 388 |
+
"raw_weekly": server_metrics.get("weekly", 0),
|
| 389 |
+
"raw_monthly": server_metrics.get("monthly", 0)
|
| 390 |
+
}
|
| 391 |
+
})
|
| 392 |
+
|
| 393 |
+
print(f"DEBUG: Returning {len(results)} servers")
|
| 394 |
+
return {
|
| 395 |
+
"servers": sorted(results, key=lambda x: x["name"]),
|
| 396 |
+
"system": get_system_metrics()
|
| 397 |
+
}
|
| 398 |
+
except Exception as e:
|
| 399 |
+
import traceback
|
| 400 |
+
traceback.print_exc()
|
| 401 |
+
return {"error": str(e), "servers": []}
|
| 402 |
|
| 403 |
@app.get("/api/usage")
|
| 404 |
async def get_usage_trends(range: str = "24h"):
|