mcp-gateway
by shamalawy
README.md
# MCP Gateway
A self-hosted control plane for [Model Context Protocol](https://modelcontextprotocol.io) servers.
Define every MCP server you use in one place, manage them from a web GUI, and give your agents a
single endpoint — per server or for a whole *group* of servers with namespaced tools.
```
Claude Code · Claude Desktop · Cursor · VS Code · your agent
│ Streamable HTTP /mcp/servers/<id> /mcp/groups/<id> (+ API key)
▼
┌────────────────────── MCP Gateway ──────────────────────┐
│ web GUI · admin REST API · sessions · logs │
│ groups: namespaced tools pg-analytics__execute_sql … │
│ upstream manager: start/stop, restart w/ backoff, │
│ health checks, idle stop, tool filters & renames │
└───────┬──────────────────┬──────────────────┬───────────┘
stdio (npx/uvx/docker) Streamable HTTP legacy SSE
Postgres, filesystem… remote MCP servers older servers
```
**Scenario.** An agent needs two Postgres databases. You add `pg-analytics` and `pg-orders` in the
GUI (from the *PostgreSQL (DBHub)* template), put them in a group `databases`, and point the agent at
`http://gateway:8808/mcp/groups/databases`. The agent sees `pg-analytics__execute_sql` and
`pg-orders__execute_sql`; the gateway owns the processes, credentials, restarts and logs.
A complete, runnable version lives in [`examples/postgres-scenario`](examples/postgres-scenario).
## Features
* **Any upstream transport** — stdio processes (`npx`, `uvx`, `docker run`, anything), Streamable HTTP and
legacy SSE remotes with bearer/basic/header auth.
* **Lifecycle management** — auto/lazy start, idle stop, restart with exponential backoff and a cap,
health-check pings, startup/request/tool-call timeouts, captured stderr.
* **Groups** — several servers behind one endpoint; tools and prompts namespaced (`prefix__tool`, `.` or `-`),
or pass-through with conflict handling; per-member filters; combined instructions; implicit `all` group.
* **Tool policy** — include/exclude globs, hide, rename and re-describe tools per server.
* **Full MCP proxying** — tools, resources (+templates, subscriptions), prompts, completions, logging,
progress, cancellation, `list_changed` propagation, and server→client sampling / elicitation / roots
routed back to the originating session.
* **Downstream transports** — Streamable HTTP (stateful with resumable streams, or stateless), legacy SSE,
plus `mcp-gateway-connect`, a stdio bridge for clients such as Claude Desktop.
* **Security** — API keys (hashed, scoped to servers/groups, expiring), admin token for the GUI/API,
secret redaction, `${ENV}` interpolation so secrets never have to be written to the config,
DNS-rebinding protection, CORS allow-list.
* **GUI** — dashboard, server catalog with 16 templates (Postgres ×3, MySQL, SQLite, filesystem, GitHub,
Playwright, Context7, fetch, memory, …), tabbed editor with *Test connection*, tool explorer with
*Try it*, resources/prompts browsers, live logs, metrics, per-client connect snippets, sessions,
settings, import/export (Claude Desktop / Cursor / VS Code `mcpServers` files).
## Screenshots
The dashboard — running servers, groups, active sessions and a live event feed:

The server catalog, with transport, status, tool counts and restart counts at a glance:

A server's detail view — runtime and endpoint facts, plus the tool explorer for calling
tools straight from the GUI:

## Quick start
Requirements: Node.js ≥ 20 (plus `npx`/`uvx`/`docker` for whichever upstream servers you run).
```bash
git clone <this repo> mcp-gateway && cd mcp-gateway
npm install
npm run build
npm start # → http://127.0.0.1:8808
```
Then in the GUI: **Servers → Add server → PostgreSQL (DBHub)** → paste a DSN → *Test connection* →
*Create server*. Open the server's **Connect** tab and copy the snippet for your client, e.g.
```bash
claude mcp add --transport http pg-analytics http://127.0.0.1:8808/mcp/servers/pg-analytics
```
Development mode (Vite with hot reload on :5173, gateway on :8808): `npm run dev`.
### Docker
```bash
docker compose up -d --build # GUI on http://localhost:8808, data in ./data
```
The image ships `node`/`npx`, `uvx` and `git`, so npm- and Python-based MCP servers can be launched
inside the container. Set `GATEWAY_ADMIN_TOKEN` in the environment when the port is reachable by
others. To pre-load a configuration, mount a file and set `GATEWAY_SEED_CONFIG=/path/to/config.json`
— it is copied to the data volume on first start.
## How it fits together
| Concept | What it is |
|---|---|
| **Server** | One upstream MCP server definition: transport, lifecycle, tool policy, forwarding options. Endpoint `/mcp/servers/<id>`. |
| **Group** | An ordered set of servers exposed as one virtual MCP server with namespaced tools/prompts and merged resources. Endpoint `/mcp/groups/<id>`. The implicit group `all` contains every enabled server. |
| **API key** | Bearer token for `/mcp/*` (hashed at rest, scoped to servers/groups, optional expiry). Enforced when *Require API key* is on. |
| **Session** | One connected downstream client (Streamable HTTP or SSE). Visible and closable in the GUI. |
| **Admin token** | Protects the GUI and `/api/*`. Set via `GATEWAY_ADMIN_TOKEN` or Settings. |
Configuration is a single JSON file (`data/config.json`, mode 0600, atomic writes, `.bak` kept) —
see [docs/CONFIG.md](docs/CONFIG.md) for every field. Client setup for each tool is in
[docs/CLIENTS.md](docs/CLIENTS.md); the full design is in [docs/SPEC.md](docs/SPEC.md).
The GUI also has a built-in **Docs** page (`/docs`, next to Settings) that walks through how the
gateway works and how to add, customize and connect servers.
## CLI
```
mcp-gateway serve [--host 127.0.0.1] [--port 8808] [--data-dir ./data] [--config <file>] [--public-url <url>] [--log-level info]
mcp-gateway import <file> [--format gateway|mcpServers] [--mode merge|replace] # into the config file (restart to apply)
mcp-gateway export [--format gateway|mcpServers] [--secrets] [--out <file>]
mcp-gateway hash-token <token> # sha256 for settings.auth.adminTokenHash
mcp-gateway connect <url> [--api-key K] [--header 'Name: v'] [--sse] # stdio ⇄ HTTP bridge
mcp-gateway-connect <url> … # same bridge as a standalone package (@mcp-gateway/connect)
```
Environment: `GATEWAY_HOST`, `GATEWAY_PORT`, `GATEWAY_DATA_DIR`, `GATEWAY_CONFIG`, `GATEWAY_ADMIN_TOKEN`,
`GATEWAY_PUBLIC_URL`, `GATEWAY_LOG_LEVEL`, `GATEWAY_REQUIRE_API_KEY`, `GATEWAY_WEB_DIR`,
`GATEWAY_SEED_CONFIG` (Docker). Precedence: CLI flags > environment > `config.json` > defaults.
## Endpoints
| Path | Purpose |
|---|---|
| `POST/GET/DELETE /mcp/servers/:id` · `/mcp/groups/:id` | Streamable HTTP MCP endpoints |
| `GET /mcp/servers/:id/sse` + `POST …/messages?sessionId=` (same for groups) | legacy HTTP+SSE |
| `GET /api/health` | liveness (unauthenticated) |
| `/api/servers`, `/api/groups`, `/api/keys`, `/api/sessions`, `/api/logs`, `/api/settings`, `/api/templates`, `/api/config/{export,import}`, `/api/connect/:kind/:id` | admin REST API (JSON; `Authorization: Bearer <admin token>` when set) |
| `GET /api/events` | Server-Sent Events feed (status, logs, sessions, metrics) used by the GUI |
| `/` | the web GUI |
The complete route list with request/response shapes is in [docs/SPEC.md §7](docs/SPEC.md).
## Security notes
* With no admin token the GUI/API are open: keep the default loopback bind, or set `GATEWAY_ADMIN_TOKEN`.
* Turn on *Require API key* before exposing `/mcp` beyond localhost; scope keys to the servers/groups each agent needs.
* Secrets: prefer `${VAR}` references over literal values; values under secret-looking keys are redacted in API responses and exports (`?secrets=1` / *with secrets* export includes them).
* Put a TLS-terminating reverse proxy in front for anything internet-facing (needed for the Anthropic API MCP connector).
* The gateway executes the commands you configure — anyone with admin access can run arbitrary processes on the host.
## Development
```bash
npm run dev # gateway (tsx watch) + Vite dev server
npm run typecheck # all workspaces
npm test # vitest: shared unit tests, server integration tests (real stdio/HTTP fixtures), bridge tests
npm run build # server bundle (esbuild), connect bundle, web (vite)
```
Layout: `packages/shared` (zod config schema, DTOs, templates, naming helpers — browser-safe),
`packages/server` (Express 5 + `@modelcontextprotocol/sdk`: upstream manager, proxy server, sessions,
REST/MCP routes, CLI), `packages/web` (React 19 + Vite), `packages/connect` (stdio bridge),
`examples/postgres-scenario` (docker-compose demo).
## Limitations / roadmap
* OAuth to upstream servers is not implemented (use bearer/basic/header auth); OAuth *for* downstream clients is not implemented (API keys instead).
* Resources are not namespaced in groups — URIs are routed to their owning server; identical URIs across members resolve to the first member.
* No rate limiting; put a reverse proxy in front if you need it.
* Single-process, in-memory sessions and logs (a restart drops sessions; clients re-initialize).
## License
Apache License 2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues