diff --git a/backend/main.py b/backend/main.py index a5259eb..13eacee 100644 --- a/backend/main.py +++ b/backend/main.py @@ -18,6 +18,7 @@ metrics, rerank, tokenization, + mcp, ) settings = get_settings() @@ -69,6 +70,7 @@ async def backend_http_error_handler(request: Request, exc: BackendHTTPError): app.include_router(metrics.router) app.include_router(rerank.router) app.include_router(tokenization.router) +app.include_router(mcp.router) if __name__ == "__main__": import uvicorn diff --git a/backend/routers/mcp.py b/backend/routers/mcp.py new file mode 100644 index 0000000..4298705 --- /dev/null +++ b/backend/routers/mcp.py @@ -0,0 +1,34 @@ +import aiohttp +from fastapi import APIRouter, Request, Depends +from fastapi.responses import Response, JSONResponse +from backend.middleware.auth import require_auth +from backend.services.mcp_service import mcp_proxy, list_mcp_servers + +router = APIRouter() + + +@router.get("/v1/mcp") +async def get_mcp_servers(token: str = Depends(require_auth)): + return {"servers": list_mcp_servers()} + + +@router.post("/v1/mcp/{owner}/{repo}") +async def mcp_endpoint( + owner: str, + repo: str, + request: Request, + token: str = Depends(require_auth), +): + body = await request.body() + try: + data, status = await mcp_proxy(owner, repo, body) + return Response( + content=data, + status_code=status, + media_type="application/json", + ) + except aiohttp.ClientError: + return JSONResponse( + status_code=404, + content={"error": f"MCP server '{owner}/{repo}' not reachable"}, + ) diff --git a/backend/services/mcp_service.py b/backend/services/mcp_service.py new file mode 100644 index 0000000..d01bfba --- /dev/null +++ b/backend/services/mcp_service.py @@ -0,0 +1,29 @@ +import aiohttp + +NAMESPACE = "rob-poc" +MCP_PORT = 8080 + +# Hardcoded MCP server registry: owner/repo -> K8s service DNS +MCP_SERVERS = { + "alan5543/calculator-mcp": f"http://tool-gym-alan5543-calculator-mcp-dev.{NAMESPACE}.svc.cluster.local:{MCP_PORT}", +} + + +async def mcp_proxy(owner: str, repo: str, body: bytes) -> tuple[bytes, int]: + """Forward a JSON-RPC request to the MCP server. Returns (body, status).""" + url = MCP_SERVERS.get(f"{owner}/{repo}") + if not url: + return b'{"error":"MCP server not found"}', 404 + async with aiohttp.ClientSession() as session: + async with session.post( + url, + data=body, + headers={"Content-Type": "application/json"}, + timeout=aiohttp.ClientTimeout(total=30), + ) as resp: + data = await resp.read() + return data, resp.status + + +def list_mcp_servers() -> list[dict]: + return [{"owner": k.split("/")[0], "repo": k.split("/")[1]} for k in MCP_SERVERS]