Skip to main content
ClaudeWave

Serve multiple stdio MCP servers from one container: path routing, hub meta-tools, OAuth 2.1 + API tokens for ChatGPT, Claude, Cursor and other MCP clients

MCP ServersRegistry oficial0 estrellas0 forksTypeScriptMITActualizado today
ClaudeWave Trust Score
95/100
Verified
Passed
  • Open-source license (MIT)
  • Actively maintained (<30d)
  • Clear description
  • Topics declared
  • Documented (README)
Last scanned: 8/19/2026
Install in Claude Code / Claude Desktop
Method: NPX · @ni-c/mcp-hub
Claude Code CLI
claude mcp add mcp-hub -- npx -y @ni-c/mcp-hub
claude_desktop_config.json (Claude Desktop)
{
  "mcpServers": {
    "mcp-hub": {
      "command": "npx",
      "args": ["-y", "@ni-c/mcp-hub"],
      "env": {
        "EXTERNAL_URL": "<external_url>"
      }
    }
  }
}
1. Run the command above in your terminal (Claude Code), or paste the JSON config into claude_desktop_config.json (Claude Desktop).
2. Replace any <placeholder> values with your API keys or paths.
3. Restart Claude. The MCP server and its tools appear automatically.
Detected environment variables
EXTERNAL_URL
Casos de uso

Resumen de MCP Servers

# mcp-hub

[![CI](https://github.com/ni-c/mcp-hub/actions/workflows/ci.yml/badge.svg)](https://github.com/ni-c/mcp-hub/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/%40ni-c%2Fmcp-hub)](https://www.npmjs.com/package/@ni-c/mcp-hub)
[![npm downloads](https://img.shields.io/npm/dm/%40ni-c%2Fmcp-hub)](https://www.npmjs.com/package/@ni-c/mcp-hub)
[![Container](https://img.shields.io/badge/ghcr.io-ni--c%2Fmcp--hub-2496ED?logo=docker&logoColor=white)](https://github.com/ni-c/mcp-hub/pkgs/container/mcp-hub)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Docs](https://img.shields.io/badge/docs-mcp--hub.ni--c.de-4f46e5)](https://mcp-hub.ni-c.de)

📖 **Full documentation: <https://mcp-hub.ni-c.de>**

Serve many stdio MCP servers from **one container**, published over HTTPS for
ChatGPT connectors, Claude (Web and Code), Mistral Le Chat, Cursor, LibreChat
and any other Streamable-HTTP MCP client — with a built-in OAuth 2.1 login
protected by a single password, plus long-lived API tokens for clients that
cannot do OAuth (OpenAI Responses API, xAI API, Gemini API). Per-client
recipes: [client compatibility](https://mcp-hub.ni-c.de/guide/client-compatibility).

<!-- <picture> is resolved against the colour scheme of the page showing it, so GitHub
     picks the variant that matches its own theme toggle. npm strips <picture> and
     <source> when it sanitises the README and keeps the <img>, which is why that
     fallback brings its own dark card instead of relying on a media query. -->
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://mcp-hub.ni-c.de/architecture-dark.svg">
  <source media="(prefers-color-scheme: light)" srcset="https://mcp-hub.ni-c.de/architecture-light.svg">
  <img src="https://mcp-hub.ni-c.de/architecture.svg" alt="MCP clients connect through a reverse proxy to mcp-hub: one Node process with an OAuth 2.1 authorization server, one path per server plus the /hub aggregate, and a supervisor keeping the stdio children and remote upstreams alive" width="800">
</picture>

![Demo: config in, hub up, servers reachable through one endpoint](https://mcp-hub.ni-c.de/demo.gif)

## Why

Wrapping each stdio MCP server in its own auth-proxy container costs a full
image, an OAuth stack, a hostname and a compose stack *per server*. mcp-hub
replaces N containers with one process:

- **Config is exactly Claude Code's `mcpServers` format** — copy entries 1:1.
- **Path-based routing**: `https://host/paperless`, `https://host/homeassistant`, …
- **`/hub` aggregate**: register a *single* connector and reach every server
  through 4 meta-tools (`list_servers`, `list_tools`, `get_tool_schema`,
  `call_tool`) without flooding the model context with N×tools schemas.
- **Supervision**: children are spawned at boot, pinged, and restarted with
  exponential backoff when they die. A down server answers 503, not silence.
- **Hot reload**: edits to `mcp.json` start/stop/restart only the affected
  servers.
- **Stateless Streamable HTTP**: no session state, so claude.ai's
  reconnect-without-DELETE behaviour cannot leak processes or memory.
- **Lightweight by design**: one Node process, no database (state is one JSON
  file plus a JWT key under `/data`), a handful of runtime dependencies, and
  multi-arch images — a stated project goal is to run comfortably on a
  single-board computer like a Raspberry Pi.

## Configuration

`/config/mcp.json` — identical to Claude Code (`${VAR}` expands from the
container environment; unknown fields are ignored by Claude Code, so the file
stays interchangeable). Install stdio server binaries at a reviewed, exact
version in your image; do not download mutable packages at runtime:

```json
{
  "mcpServers": {
    "paperless": {
      "command": "paperless-mcp",
      "args": [],
      "env": { "PAPERLESS_API_TOKEN": "${PAPERLESS_API_TOKEN}" }
    },
    "homeassistant": {
      "type": "http",
      "url": "http://homeassistant:8123/api/mcp",
      "headers": { "Authorization": "Bearer ${HA_TOKEN}" }
    },
    "private-thing": { "command": "some-mcp", "args": [], "hub": false },
    "untrusted": {
      "type": "docker",
      "image": "ghcr.io/example/untrusted-mcp@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
      "network": "none",
      "memory": "256m"
    }
  }
}
```

Stdio servers (`command`/`args`/`env`) are spawned as supervised child
processes. Remote servers (`type: "http"` or `"sse"` with `url` and optional
`headers`) are connected as MCP clients with the configured headers injected
on every request — the same supervision (ping, backoff reconnect, hot reload)
applies. Upstreams that require their own *interactive* OAuth cannot be
configured with static headers; bridge those with an
[`mcp-remote`](https://github.com/geelen/mcp-remote) stdio entry and persist
its token cache (`MCP_REMOTE_CONFIG_DIR`) under `/data`.
`"hub": false` hides a server from the `/hub` aggregate; its own path keeps
working. Reserved names: `mcp`, `hub`, `authorize`, `token`, `register`,
`login`, `consent`, `health`, `livez`, `revoke`.

All stdio children share the hub's Unix user and can read its mounted files.
Only install fully trusted stdio servers. A server with a different trust level
belongs in its own container — and it does not have to speak HTTP to get there:

- `type: "docker"` — the hub creates the container and talks **stdio across the
  container boundary** over the Docker API. No HTTP listener, no bearer token,
  no bridge process in the image. The hub itself never gets the Docker socket:
  a separate `mcp-hub-docker-proxy` container holds it and allows only the
  container operations `mcp.json` describes — nothing privileged, no host
  mounts, no other images. Credentials can live with the proxy (`secretsFrom`)
  so the hub process never holds them — and rotating one is just an edit: the
  proxy watches the file and recreates the sandbox with the new values.
- `type: "unix"` / `"tcp"` — you start the container, the hub connects to a
  socket. Costs the hub no privileges at all, and a Unix socket reaches a
  sandbox running with `network_mode: none`.

Both carry the newline-delimited JSON-RPC the specification asks custom
transports to reuse. See [sandboxing](https://mcp-hub.ni-c.de/guide/sandboxing)
and [SECURITY.md](SECURITY.md).

For a custom image, pin every package to an exact version:

```dockerfile
FROM ghcr.io/ni-c/mcp-hub:0.6.0   # pin @sha256:<digest> in production
USER root
RUN npm install -g your-mcp-package@1.2.3
USER node
```

### Environment

| Variable | Required | Description |
|---|---|---|
| `EXTERNAL_URL` | yes | Public base URL, e.g. `https://mcp.example.net` (no path) |
| `PASSWORD_HASH` | one of | bcrypt hash of the login password (`htpasswd -bnBC 10 "" 'pw' \| tr -d ':\n'`) |
| `PASSWORD` | one of | plain-text alternative to `PASSWORD_HASH` |
| `TRUSTED_PROXIES` | no | comma-separated IPs/CIDRs allowed to set `X-Forwarded-*` (see below) |
| `RESOURCE_BOUND_TOKENS` | no | RFC 8707 tokens bound to `/hub` or one `/<name>/mcp`, default `true`; set `false` only to keep pre-0.5 unbound tokens working |
| `DEFAULT_RESOURCE` | no | server name (or `hub`) to bind tokens to when a client sends no `resource` parameter; unset → such requests are refused |
| `MCP_BODY_LIMIT` | no | authenticated MCP JSON body limit, default `1mb` |
| `MCP_REQUESTS_PER_MINUTE` | no | limit per OAuth client, default `120` |
| `MCP_MAX_CONCURRENT_REQUESTS` | no | in-flight limit per OAuth client, default `4` |
| `HTTP_HEADERS_TIMEOUT_MS` | no | Node HTTP header timeout, default `10000` |
| `HTTP_REQUEST_TIMEOUT_MS` | no | complete request timeout, default `310000` (slightly above the tool-call timeout) |
| `PORT` | no | listen port (default 80 in the image, 3000 outside) |
| `CONFIG_PATH` | no | default `/config/mcp.json` |
| `DATA_PATH` | no | default `/data` |
| `LOG_FILE` | no | additionally mirror all log output into this file, e.g. `/data/mcp-hub.log` (see below) |

`/data` holds the Ed25519 JWT key, registered OAuth clients, approvals and
refresh tokens. **Mount it as a volume** — recreating it invalidates every
connector authorization.

Every access token is bound to one resource. The OAuth client includes the
resource advertised by the endpoint's RFC 9728 document — no client-side
configuration needed — and the resulting token is valid only there: a token for
`/paperless/mcp` cannot call `/hub`, `/health` or another server. The shorter
`/<name>` route is canonicalized to `/<name>/mcp`.

`RESOURCE_BOUND_TOKENS=false` turns this off and is a migration mode for
deployments from 0.4 and earlier, where tokens were issued without a resource
and reach every path. The hub logs a warning while it is set. Removing it
invalidates those unbound tokens, so every connector authorizes once more.

`TRUSTED_PROXIES` decides what `req.ip` is, and therefore what the login rate
limiter counts. List **only** your own reverse proxy, and make sure it
*overwrites* `X-Forwarded-For` rather than appending to it — otherwise a
client can supply its own address and rotate it to sidestep the per-IP limit.
If the variable is unset, every request appears to come from the proxy and
per-IP limiting degrades to a single global counter (the hub logs a warning
at startup). A global cap of 100 failures per 15 minutes applies either way.

## Running

### Option A — prebuilt image from GHCR (recommended)

Published on every push to `main` and every `vX.Y.Z` release tag, for
`linux/amd64` and `linux/arm64`. Browse the versions on the
[package page](https://github.com/ni-c/mcp-hub/pkgs/container/mcp-hub).

```sh
docker pull ghcr.io/ni-c/mcp-hub:0.6.0
```

Tags: `latest` (tip of `main`), `X.Y.Z` and `X.Y` (releases), and
`sha-<commit>` for a specific build.

Use a version tag instead of `latest` for controlled updates. For an immutable
deployment, record the resolved digest from `docker image inspect` and use
`ghcr.io/ni-c/mcp-hub:<version>@
aichatgptclaudeclaude-codedockermcpmcp-gatewaymcp-servermodel-context-protocoloauth2self-hostedstreamable-http

Lo que la gente pregunta sobre mcp-hub

¿Qué es ni-c/mcp-hub?

+

ni-c/mcp-hub es mcp servers para el ecosistema de Claude AI. Serve multiple stdio MCP servers from one container: path routing, hub meta-tools, OAuth 2.1 + API tokens for ChatGPT, Claude, Cursor and other MCP clients Tiene 0 estrellas en GitHub y su última actualización registrada es del 2026-08-19.

¿Cómo se instala mcp-hub?

+

Puedes instalar mcp-hub clonando el repositorio (https://github.com/ni-c/mcp-hub) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.

¿Es seguro usar ni-c/mcp-hub?

+

Nuestro agente de seguridad ha analizado ni-c/mcp-hub y le ha asignado un Trust Score de 95/100 (tier: Verified). Revisa el desglose completo de comprobaciones superadas y flags en esta página.

¿Quién mantiene ni-c/mcp-hub?

+

ni-c/mcp-hub es mantenido por ni-c. La última actividad registrada en GitHub es del 2026-08-19, con 3 issues abiertos.

¿Hay alternativas a mcp-hub?

+

Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.

Despliega mcp-hub en tu cloud

Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.

¿Mantienes este repo? Añade un badge a tu README

Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.

Featured on ClaudeWave: ni-c/mcp-hub
[![Featured on ClaudeWave](https://claudewave.com/api/badge/ni-c-mcp-hub)](https://claudewave.com/repo/ni-c-mcp-hub)
<a href="https://claudewave.com/repo/ni-c-mcp-hub"><img src="https://claudewave.com/api/badge/ni-c-mcp-hub" alt="Featured on ClaudeWave: ni-c/mcp-hub" width="320" height="64" /></a>

Más MCP Servers

Alternativas a mcp-hub