Files
fkrebs a0f1707e33 decommission Wallabag: remove from portmap, databases, mcp-gateway docs
Wallabag replaced by crawl4ai in paywall-bypass n8n flow.
Container, DB, MCP server, and Bifrost client all removed 2026-05-28.
2026-05-28 00:31:13 +02:00

207 lines
8.2 KiB
Markdown

# 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 at `data/config.db`.
- **Endpoint**: `https://ai.nuclide.systems/mcp` (Zoraxy → `192.168.1.40:14003`)
- **Auth**: Virtual Key (`sk-bf-` prefix) via `Authorization: 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 via `tools_to_execute_json`.
- **Internal upstreams**: All child MCP containers run on the `ai-internal` Docker network at `http://<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`:
```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:
```bash
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:
```bash
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
```bash
# 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
```bash
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`
1. `auth_type=none` (not `per_user_oauth`)
2. `allow_on_all_virtual_keys=true`
3. `tools_to_execute_json` populated (tool names without client prefix)
4. 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-26` on CT 108)
- Container stopped; compose file renamed to `docker-compose.yml.DECOMMISSIONED-2026-05-26`
- Pocket-ID clients `e73bb7b9` (litellm/mcp-gateway) and `78c78998` (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` |