Wallabag replaced by crawl4ai in paywall-bypass n8n flow. Container, DB, MCP server, and Bifrost client all removed 2026-05-28.
8.2 KiB
MCP Gateway — Bifrost (migrated 2026-05-26)
MCP servers are now aggregated by Bifrost at https://ai.nuclide.systems/mcp.
The legacy FastAPI DinD mcp-gateway (mcp.nuclide.systems) is pending decommission.
Architecture
- Stack:
ai/bifrost/on CT 104 — Bifrost LLM+MCP gateway, SQLite state atdata/config.db. - Endpoint:
https://ai.nuclide.systems/mcp(Zoraxy →192.168.1.40:14003) - Auth: Virtual Key (
sk-bf-prefix) viaAuthorization: Bearer <vk>. - Client model: Each upstream MCP server is a registered client in Bifrost (
auth_type=none,allow_on_all_virtual_keys=true). Tools are auto-discovered and enabled viatools_to_execute_json. - Internal upstreams: All child MCP containers run on the
ai-internalDocker network athttp://<name>-mcp:8000/mcp(streamable-HTTP) or as dedicated stacks.
Virtual keys
| Name | Key prefix | Use |
|---|---|---|
claude-code |
sk-bf-bfc19117-4c46-4d48-9b10-85d78b1ae2b3 |
Claude Code + Claude.ai |
mcp-dev |
sk-bf-279d3ecc-9031-41ff-a582-ecf3dca61d52 |
Testing / dev |
open-webui |
sk-bf-7e6999fc-2d86-48f9-8ac9-0ba558893d87 |
Open WebUI (LITELLM_API_KEY in ai/.env) |
Server inventory (as of 2026-05-26)
29 connected clients, ~760 tools total.
| Client name | Upstream | Notes |
|---|---|---|
bluesky |
http://ariel-mcp:8000/mcp |
|
coder |
http://coder-mcp:8000/mcp |
|
comfyui |
http://comfyui-mcp:8000/mcp |
Intel Arc image gen |
context7 |
http://mcp-context7:8000/mcp |
|
crawl4ai |
http://mcp-crawl4ai:11235/mcp/sse (SSE) |
|
docling |
http://docling-mcp:8000/mcp |
PDF→Markdown |
fetch |
http://mcp-fetch:8000/mcp |
|
git |
http://mcp-git:8000/mcp |
|
gitea |
http://gitea-mcp:8000/mcp |
|
gitlab |
http://mcp-gitlab:8000/mcp |
GITLAB_API_URL=https://gitlab.dlr.de/api/v4 |
gotify |
http://mcp-gotify:8000/mcp |
|
home_assistant |
http://192.168.1.60:9583/private_ehnWeRl2G3De6NnbcN7teQ |
HA add-on; no TLS |
immich |
http://mcp-immich:8000/mcp |
|
kroki |
http://kroki-mcp:8000/mcp |
Diagram rendering |
markitdown |
http://mcp-markitdown:8000/mcp |
|
memos |
http://mcp-memos:8000/mcp |
|
nextcloud |
http://mcp-nextcloud:8000/mcp |
|
ntfy |
http://mcp-ntfy:8000/mcp |
|
obsidian |
http://mcp-obsidian:8000/mcp |
Vault at Nextcloud/UNAS |
paper_search |
http://mcp-paper-search:8000/mcp |
|
paperless |
http://paperless-mcp:8000/mcp |
|
proxmox |
http://mcp-proxmox:8000/mcp |
Read-only (PVEAuditor) |
searxng |
http://mcp-searxng:8000/mcp |
|
sequential_thinking |
http://mcp-sequential-thinking:8000/mcp |
|
time |
http://mcp-time:8000/mcp |
|
unifi |
http://mcp-unifi:8000/mcp |
|
upload_artifact |
http://upload-artifact-mcp:8000/mcp |
S3 via Garage |
wikipedia |
http://mcp-wikipedia-mcp:8000/mcp |
|
youtube_transcript |
http://mcp-youtube-transcript:8000/mcp |
Not yet connected
| Client | Reason |
|---|---|
n8n — https://n8n.nuclide.systems/mcp-server/http |
Streamable-HTTP transport: POST returns SSE stream, Bifrost HTTP client times out. |
shepard — https://shepard.nuclide.systems/v2/mcp |
Same streamable-HTTP issue. Bearer token stored in /tmp/migrate_mcp_oauth.py. |
Client configuration
Claude Code
Add to ~/.claude.json or project .mcp.json:
{
"mcpServers": {
"nuclide": {
"type": "http",
"url": "https://ai.nuclide.systems/mcp",
"headers": {
"Authorization": "Bearer sk-bf-bfc19117-4c46-4d48-9b10-85d78b1ae2b3"
}
}
}
}
Claude.ai
Settings → Integrations → Add MCP server:
- URL:
https://ai.nuclide.systems/mcp - Header:
Authorization: Bearer sk-bf-279d3ecc-9031-41ff-a582-ecf3dca61d52
LLM Inference & Governance
Bifrost proxies LLM inference at /v1 (OpenAI-compatible). enforce_auth_on_inference=1 — all /v1 calls require a valid sk-bf-* VK.
Providers
| Provider | Internal name | Base URL | Notes |
|---|---|---|---|
| SAIA (GPU cluster) | openai |
https://chat-ai.academiccloud.de/v1 |
Rate limited (see below) |
| Google Gemini | gemini |
default | |
| Mistral AI | mistral |
default | |
| Cerebras | cerebras |
default | |
| claude-max-bridge | openrouter |
http://claude-max-bridge:8000 |
Claude Opus/Sonnet/Haiku via max subscription |
SAIA rate limits
SAIA enforces per-account quotas. Bifrost is configured with a global provider-level limit (config_providers.rate_limit_id='saia-minute'):
| Window | SAIA limit | Bifrost enforcement |
|---|---|---|
| Per minute | 30 req | ✅ active (saia-minute row) |
| Per hour | 200 req | row exists (saia-hour), not linked |
| Per day | 1000 req | row exists (saia-day), not linked |
| Per month | 3000 req | not trackable across restarts |
Note: Bifrost only supports one rate limit window per provider. The minute window is linked because it provides burst protection. The saia-hour / saia-day rows are in governance_rate_limits and can be linked by updating config_providers SET rate_limit_id='saia-hour' if needed.
To change the active window:
sqlite3 /opt/stacks/ai/bifrost/data/config.db \
"UPDATE config_providers SET rate_limit_id='saia-day' WHERE name='openai';"
docker compose -f /opt/stacks/ai/bifrost.yml up -d --force-recreate
Rate limit API: GET /api/governance/rate-limits is read-only. POST/PUT return 405. Use direct SQLite to create new entries.
Virtual key governance
All VKs (governance_virtual_key_provider_configs) have:
allow_all_keys=1— set via SQL (Bifrost API PUT silently ignores this field)allowed_models— explicit JSON model list as text in DB (SQL NULL = deny all with enforce_auth_on_inference=1)
If models stop working after a Bifrost upgrade/restore, re-run /tmp/fix_vk_final.py on CT 104 and then:
sqlite3 /opt/stacks/ai/bifrost/data/config.db \
"UPDATE governance_virtual_key_provider_configs SET allow_all_keys=1;"
docker compose -f /opt/stacks/ai/bifrost.yml up -d --force-recreate
Ops
# On CT 104
cd /opt/stacks/ai/bifrost
docker compose up -d --force-recreate
docker logs bifrost -f
# Inspect config DB
sqlite3 data/config.db '.tables'
sqlite3 data/config.db 'SELECT name, auth_type, allow_on_all_virtual_keys FROM config_mcp_clients;'
# Count active tools via API
curl -s http://localhost:14003/mcp \
-H "Authorization: Bearer sk-bf-279d3ecc-9031-41ff-a582-ecf3dca61d52" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | python3 -m json.tool | grep '"name"' | wc -l
Add a new MCP client
curl -sc /tmp/bfcookies http://localhost:14003/api/session/login \
-H "Content-Type: application/json" \
-d '{"username":"fkrebs","password":"tapirnase"}' > /dev/null
curl -s -X POST http://localhost:14003/api/mcp/client \
-b /tmp/bfcookies \
-H "Content-Type: application/json" \
-d '{
"name": "<name>",
"connection_type": "http",
"connection_string": "http://<host>:8000/mcp",
"auth_type": "none",
"allow_on_all_virtual_keys": true
}'
Wait ~10 s for tool discovery, then enable tools via PUT /api/mcp/client/<id> with tools_to_execute.
See /tmp/add_all_mcp_clients.py on CT 104 for a complete example.
Key requirements for tools to appear in /mcp tools/list
auth_type=none(notper_user_oauth)allow_on_all_virtual_keys=truetools_to_execute_jsonpopulated (tool names without client prefix)- VK must have
sk-bf-prefix
Legacy mcp-gateway (FastAPI / DinD)
Status: DECOMMISSIONED 2026-05-26.
- Zoraxy route removed (config renamed to
.DECOMMISSIONED-2026-05-26on CT 108) - Container stopped; compose file renamed to
docker-compose.yml.DECOMMISSIONED-2026-05-26 - Pocket-ID clients
e73bb7b9(litellm/mcp-gateway) and78c78998(Claude MCP) deleted from CT 110 DB
Bifrost OIDC client (Pocket ID)
Used for per_user_oauth flows (not currently active — all clients use auth_type=none):
| Field | Value |
|---|---|
| Client ID | ec0d15e6-e86d-49b0-ac12-cdfb5afc9086 |
| Authorize URL | https://id.nuclide.systems/authorize |
| Token URL | https://id.nuclide.systems/api/oidc/token |
mcp_external_client_url |
https://ai.nuclide.systems |