ruflo/docs/adr/ADR-032-RVF-PRIVATE-MCP-TUNNEL.md
Implemented
HuggingFace Chat UI enforces HTTPS-only for MCP server URLs as SSRF protection (urlSafety.isValidUrl()). In containerized deployments (Docker Compose), the MCP bridge runs on a private Docker network (http://mcp-bridge:3001/mcp) — not exposed to the internet.
This creates a conflict: the security control blocks legitimate internal service communication.
Use an RVF-inspired private tunnel pattern — patch the URL safety validation at build time to allow HTTP for admin-configured MCP_SERVERS on the private container network.
┌──────────────────────────────────────────────────────────────┐
│ Private Docker Network │
│ │
│ ┌──────────────┐ HTTP (private) ┌──────────────────────┐ │
│ │ Chat UI │──────────────────►│ MCP Bridge │ │
│ │ :3000 │ MCP JSON-RPC │ :3001 │ │
│ │ │ │ │ │
│ │ RVF Patch: │ /chat/completions│ ├─ /mcp (tools) │ │
│ │ Allow HTTP │──────────────────►│ ├─ /models │ │
│ │ for private │ │ ├─ /chat/completions │ │
│ │ network │ │ └─ /health │ │
│ └──────────────┘ └──────────────────────┘ │
│ │ │ │
│ └──── Not exposed to internet ────────┘ │
└──────────────────────────────────────────────────────────────┘
│
Port 3000 (only this is exposed to host)
| RVF Segment | Application |
|---|---|
| WASM_SEG (0x10) | Lightweight query microkernel — MCP bridge acts as the runtime |
| CRYPTO_SEG (0x0C) | Request signing between kernel and bridge (optional) |
| META_IDX_SEG (0x0D) | Tool registry cache in bridge /models endpoint |
| KERNEL_SEG (0x0E) | Docker container as execution boundary |
mcp-bridge:3001) is only reachable within the Docker network, not from the internetMCP_SERVERS is set by the deployment operator in docker-compose.yml, not by end userschat-ui/patch-mcp-url-safety.sh:
# Allow http: protocol for private network MCP
sed -i 's/url.protocol !== "https:"/url.protocol !== "https:" \&\& url.protocol !== "http:"/' "$URLSAFETY_FILE"
# Allow localhost for container-internal servers
sed -i 's/hostname === "localhost"/false \&\& hostname === "localhost"/' "$URLSAFETY_FILE"
chat-ui/Dockerfile:
USER root
COPY patch-mcp-url-safety.sh /tmp/patch-mcp-url-safety.sh
RUN sh /tmp/patch-mcp-url-safety.sh && rm /tmp/patch-mcp-url-safety.sh
USER 1000
docker-compose.yml:
chat-ui:
environment:
MCP_SERVERS: '[{"name":"Tools","url":"http://mcp-bridge:3001/mcp"}]'
urlSafety file naming[MCP] Loaded 1 server(s): AI Assistant Tools
Listening on http://0.0.0.0:3000
Models: gemini-2.5-pro, gemini-2.5-flash, gpt-4.1, gpt-4.1-mini, gpt-4o, gpt-4o-mini, o3-mini, o1-mini
Bridge health: ok (3 tools: search, web_research, system_guide)
Chat completions: working via Gemini proxy