Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
metrics,
rerank,
tokenization,
mcp,
)

settings = get_settings()
Expand Down Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions backend/routers/mcp.py
Original file line number Diff line number Diff line change
@@ -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"},
)
29 changes: 29 additions & 0 deletions backend/services/mcp_service.py
Original file line number Diff line number Diff line change
@@ -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]
Loading