Skip to main content
Glama
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:

![MCP Gateway dashboard](docs/images/dashboard.png)

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

![Server list](docs/images/servers.png)

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

![Server detail and tool explorer](docs/images/server-detail.png)

## 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).