4.6 KiB
4.6 KiB
MCP Gateway v2 — Operational
Finished implementation of the DinD MCP gateway (Option A). Supersedes the
"Phase 1" stub described in mcp-gateway-requirements.md.
What it is
ai/mcp-gateway/ — FastAPI app, container mcp-gateway, public via Zoraxy at
https://mcp.nuclide.systems (→ 192.168.1.40:8080). Single OAuth-gated
entrypoint fronting several MCP servers.
Architecture (as built)
- Spawn model: Docker-in-Docker. Each spawnable server runs as
mcp-<name>on theai-internalnetwork with no host ports (fixes the old8000:8000collision — servers talk over internal DNS). - Transport bridge: generic streaming reverse proxy
https://mcp.nuclide.systems/<server>/<path>→http://<upstream>/<path>. Transport-agnostic — works for streamable-HTTP (/mcp) and legacy SSE (/sse+/messages). Registered LAST so specific routes win. - Auth: Pocket ID. Two paths off the same OIDC app:
- MCP clients / API / proxy —
Bearertoken, validated via OIDC userinfo (300s TTL cache).401carriesWWW-Authenticate+ RFC 9728/.well-known/oauth-protected-resourcefor discovery. - Browser Web UI — full Authorization-Code flow with an httpOnly
session cookie: unauthenticated
/ui→302 /login→ Pocket IDauthorize(CSRFstatecookie) →/sso/callback(state-checked code→token exchange) sets themcp_sessioncookie and shows the page + a copyable Bearer token for MCP clients./logoutclears it. The UI and/api/*accept either the cookie or a Bearer header; the/{srv}/proxy stays Bearer-only./health+ the well-known metadata are open.
- MCP clients / API / proxy —
- Secrets: all per-server OAuth client secrets come from
ai/.env(env_file: ../.env), read viaos.environ— none hardcoded in source.
Server registry (SERVERS in server.py)
| name | kind | upstream | notes |
|---|---|---|---|
| comfyui | static | http://comfyui-mcp:8000 |
runs as its own stack (ai/comfyui-mcp.yml); gateway only proxies |
| nextcloud | spawn | mcp-nextcloud:8000 |
ghcr.io/cbcoutinho/nextcloud-mcp-server streamable-http |
| crawl4ai | spawn | mcp-crawl4ai:11235 |
unclecode/crawl4ai |
| mermaid | spawn | mcp-mermaid:8000 |
node:20-slim + npx mcp-mermaid (validated working) |
| markitdown | spawn | mcp-markitdown:8000 |
ghcr.io/astral-sh/uv + uvx markitdown-mcp |
| papersearch | spawn | mcp-papersearch:8000 |
python + uv + mcp-proxy → paper_search_mcp |
Verified
/health200 (no auth);/.well-known/oauth-protected-resourceJSON (no auth)./mcp.json,/<server>/mcp→ 401 without token; 401 with bad token (userinfo path); correctWWW-Authenticate.- Upstream path proven: gateway →
comfyui-mcp/mcpinitialize → 200; gateway → spawnedmcp-mermaid/mcp→ 200 (spawn pattern + proxy). - Route precedence correct (specific routes beat the catch-all proxy).
Not yet exercised (needs a real token / operational bring-up)
- Token-authenticated end-to-end requires an interactive Pocket ID login.
Get a token: open
https://mcp.nuclide.systems/sso/callbackvia the OAuth authorize flow (or any Pocket ID token with scopesopenid,mcp); the callback page prints the access token. - Bring up the 4 remaining spawnable servers (nextcloud/crawl4ai/
markitdown/papersearch):
POST /api/servers/<name>/start(bearer) orPOST /api/servers/bulk/start. Each may need a transport/flag tweak on first run — mermaid is the proven reference.
Client registration
- LobeChat: Settings → Skills → Skill Store → Custom → Import JSON, e.g.
(Or, for ComfyUI specifically, connect LobeChat straight to
{ "mcpServers": { "comfyui": { "type": "http", "url": "https://mcp.nuclide.systems/comfyui/mcp", "auth": { "type": "bearer", "accessToken": "<pocket-id token>" } } } }http://comfyui-mcp:8000/mcpinternally — seecomfyui-mcp.md— no token.) - Claude.ai: add
https://mcp.nuclide.systems/<server>/mcpas a custom connector; it will discover auth via the protected-resource metadata.
Ops
cd /opt/stacks/ai/mcp-gateway && docker compose up -d --build- Logs:
docker logs mcp-gateway; UI:https://mcp.nuclide.systems/ui(Pocket-ID-gated;/loginto sign in). - Config overlay persisted in
ai/mcp-gateway/config.json. - UI check (Playwright) — the live UI is auth-gated, so the check
serves the local template + mocks
/api/servers, screenshots, and asserts cards render:uv run --with playwright -- python -m playwright install chromium # once uv run --with playwright -- python ai/mcp-gateway/ui_check.py # -> /tmp/mcp_ui.png