Universal MCP Gateway
README.md
# Universal MCP Gateway
A self-hosted **Model Context Protocol (MCP)** gateway that aggregates all your MCP servers behind a single Streamable HTTP endpoint. Includes automatic registry discovery, on-demand Docker provisioning, and multi-device support.
## Features
- **Single Endpoint** — All MCP servers exposed via one URL (`/all` for universal, `/mcp` for legacy)
- **Registry Auto-Broker** — Mirrors the official MCP Registry (19,000+ servers), provisions on demand in isolated Docker containers
- **Multi-Device** — Control server, laptop, and Windows PC from one gateway via SSH
- **SSE Keepalive** — Prevents Cloudflare/proxy idle timeouts on streaming connections
- **OAuth2 PKCE** — Optional OAuth2 authorization code flow with PKCE support
- **Workflow Engine** — Save and replay multi-step tool sequences
- **Bearer Auth** — Simple token-based authentication
## Architecture
```
mcp.yourdomain.com (:8798)
│
┌──────▼──────┐
│ MCP Router │ Path-based routing
│ router.mjs │ /all → Universal, /mcp → Legacy
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
┌─────────▼──┐ ┌─────▼────┐ ┌──▼──────────┐
│ Universal │ │ Registry │ │ Child MCPs │
│ Gateway │ │ Autobroker│ │ (remote + │
│ gateway.mjs│ │ registry- │ │ stdio) │
└────────────┘ │ manager │ └─────────────┘
└───────────┘
```
## Quick Start
```bash
# Clone
git clone https://github.com/Samuel-Mencke/mcp-gateway.git
cd mcp-gateway
# Install dependencies
npm install
# Configure
cp .env.example .env
# Edit .env — set MCP_PUBLIC_URL and generate a token
# Generate auth token
echo -n "$(openssl rand -hex 32)" > ~/.mcp-gateway/token
# Start the gateway
node gateway.mjs # Port 8799
# In another terminal:
node router.mjs # Port 8798 (public-facing)
```
## Configuration
All configuration is via environment variables. See [`.env.example`](.env.example) for all options.
### Key Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `MCP_PORT` | `8799` | Universal Gateway listen port |
| `MCP_ROUTER_PORT` | `8798` | Router listen port |
| `MCP_PUBLIC_URL` | `http://localhost:8798` | Your public URL (domain/tunnel) |
| `MCP_STATE_DIR` | `~/.mcp-gateway` | State directory (registry, tokens, etc.) |
| `MCP_BEARER_TOKEN` | (from `$MCP_STATE_DIR/token`) | Auth token |
### Multi-Device (Optional)
Set these to enable SSH-based remote control:
```bash
# Windows PC
MCP_PC_HOST=windows-host
MCP_PC_USER=username
# Linux laptop
MCP_LAPTOP_HOST=laptop-host
MCP_LAPTOP_USER=username
```
## Default MCP Servers
The gateway ships with these servers enabled by default:
- **Context7** — Current library documentation
- **Exa** — Web search and fetch
- **MCP Docs** — Official MCP documentation
- **GitHub** — Official GitHub MCP server (requires `gh auth`)
- **Playwright** — Browser automation
- **Chrome DevTools** — Debugging and performance
- **Filesystem** — Sandboxed file access
- **Memory** — Persistent knowledge graph
- **Sequential Thinking** — Structured planning
Additional servers can be provisioned on demand from the official MCP Registry via `ensure_capability`.
## Registry Auto-Broker
The gateway mirrors the complete [official MCP Registry](https://registry.modelcontextprotocol.io) and can provision any supported server on demand:
```
Agent: "I need PostgreSQL schema inspection"
Gateway: Searches registry → provisions postgres-mcp → probes handshake → ready
```
Provisioned servers run in restricted Docker containers:
- Read-only root filesystem
- No host mounts
- Dropped capabilities
- CPU/RAM/PID limits
- Blocked private/Tailscale egress
## Deployment
### systemd
```ini
# ~/.config/systemd/user/mcp-router.service
[Unit]
Description=MCP Router
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/mcp-gateway
Environment=MCP_ROUTER_PORT=8798
ExecStart=/usr/bin/node %h/mcp-gateway/router.mjs
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
```
```ini
# ~/.config/systemd/user/mcp-universal.service
[Unit]
Description=MCP Universal Gateway
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/mcp-gateway
EnvironmentFile=%h/mcp-gateway/.env
ExecStart=/usr/bin/node %h/mcp-gateway/gateway.mjs
Restart=always
RestartSec=5
TimeoutStopSec=20
[Install]
WantedBy=default.target
```
### Cloudflare Tunnel
No port-forwarding needed — use a Cloudflare Tunnel:
```yaml
# ~/.cloudflared/config.yml
ingress:
- hostname: mcp.yourdomain.com
service: http://127.0.0.1:8798
originRequest:
noTLSVerify: true
connectTimeout: 30s
keepAliveConnections: 100
keepAliveTimeout: 600s
```
## Client Configuration
The gateway works with any MCP-compatible client:
| Client | Config |
|--------|--------|
| Claude Code | `type: "url", url: "https://mcp.yourdomain.com/all"` |
| OpenAI Codex | `type: "streamable-http", url: "https://mcp.yourdomain.com/all"` |
| Cursor | `url: "https://mcp.yourdomain.com/all", transport: "streamable-http"` |
| Hermes Agent | `type: "streamable-http", url: "https://mcp.yourdomain.com/all"` |
| OpenCode | `type: "streamable-http", url: "https://mcp.yourdomain.com/all"` |
All clients need: `headers: { Authorization: "Bearer <your-token>" }`
## Files
| File | Description |
|------|-------------|
| `router.mjs` | Path-based router, OAuth2, SSE keepalive |
| `gateway.mjs` | Universal MCP gateway server |
| `registry-manager.mjs` | Registry sync, auto-provisioning, AGENTS.md generation |
| `workflow-store.mjs` | Durable workflow persistence |
| `server.mjs` | Legacy ChatGPT connector (admin/SSH tools) |
| `windows-runner.py` | Windows desktop automation runner |
## Requirements
- Node.js >= 18
- Docker (for registry auto-provisioning)
- SSH access (for multi-device support)
## License
MIT
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues