Skip to content

mcp-hubThe dual-era MCP gateway

One container for all your MCP servers, speaking both MCP revisions on every endpoint — 2026-07-28 and 2025-11-25, the client picks. Serve many stdio servers over one HTTPS endpoint for ChatGPT, Claude, Le Chat, Cursor and any other MCP client, with the Claude Code config format you already have, path-based routing, a context-friendly aggregate endpoint, and OAuth 2.1 in both directions.

mcp-hub

The shape of it ​

Request flow from an MCP client through a reverse proxy into mcp-hub, which is an OAuth 2.1 authorization server for its clients and an OAuth client toward its own upstreams, and on to its child serversMCP clientChatGPT · ClaudeCursor · Le Chat …Reverse proxyTLS terminationmcp-hubone Node processOAuth 2.1server in · client out/hub · /<name>/mcpsupervisorping · backoff restart · hot reloadpaperlessstdio childscrapersandbox containerhomeassistantremote · OAuth outTLS
One process terminates the client’s OAuth session, routes by path, and keeps every configured server alive behind it.

See it run ​

Demo: config in, hub up, servers reachable through one endpoint

Registration-free by default ​

A client no longer has to register to use this hub. It publishes a Client ID Metadata Document at a stable HTTPS URL and uses that URL as its client_id; the hub fetches the document, reads the client's name and redirect URIs out of it, and shows you the origin that vouched before you approve anything. Nothing is stored, so a client that is reinstalled or moves to another machine is still the same client — and the approval you gave it still holds.

That URL is chosen by an unauthenticated caller, so the fetch is treated as hostile: https only, no redirects, no private addresses, the checked address pinned into the connection, a hard size cap and a short timeout.

Confidential clients authenticate at the token endpoint with private_key_jwt against the keys in their own document. Everything older keeps working: RFC 7591 dynamic registration stays advertised beside it, mcp-hub-admin clients add issues credentials by hand for a client that can do neither, and one setting turns a mechanism off once you no longer need it.

The same, pointing outwards ​

The interesting half is that it works in the other direction too. A remote MCP server that requires OAuth is not a problem to be bridged around — the hub is an OAuth client in its own right:

A client connecting to the hubThe hub connecting to an upstream
Operator-issued credentialsmcp-hub-admin clients addoauth.mode: "static"
Dynamic registration (RFC 7591)POST /registeroauth.mode: "dcr"
Registration management (RFC 7592)GET/PUT/DELETE /register/<id>deleted again on upstream logout
Client ID metadata documentthe client's URL as its client_idthe hub's own document, one per upstream
Plain bearer tokenan API tokena static Authorization header

Machine-to-machine upstreams need no attention at all — the hub fetches a token and renews it. Where a person has to sign in, one command prints a URL, and the server comes up by itself once you have.

sh
mcp-hub-admin upstream login some-saas

Every standard, named and linked →

Two revisions, one gateway ​

The same trick, applied to the protocol itself. Every endpoint answers MCP 2026-07-28 and 2025-11-25 alike, and the client cannot tell from the answers which one it got. Where the two revisions genuinely differ, the hub absorbs the difference rather than passing it on:

Towards a clientTowards a child server
Elicitation — a server asking a person somethingreturned as a result on 2026-07-28, so it reaches the person instead of dying at the gatewayforwarded to the child that asked, with the answer carried back
Subscriptions — a server announcing that something changedserved as a subscriptions/listen streamsubscriptions/listen to a 2026 child, resources/subscribe to a 2025 one

That second row is the useful one in practice: most MCP servers in the wild are still on the older revision, and their notifications now reach clients that only speak the newer one.

How elicitation travels → · What a client can watch →

Try it in two minutes ​

sh
mkdir -p data && sudo chown -R 1000:1000 data   # the container runs as uid 1000

docker run -d --name mcp-hub \
  -p 127.0.0.1:7690:80 \
  -e EXTERNAL_URL="https://mcp.example.net" \
  -e PASSWORD_HASH="$(htpasswd -bnBC 10 '' 'yourpassword' | tr -d ':\n')" \
  -e TRUSTED_PROXIES="192.168.1.0/24" \
  -v "$PWD/config:/config:ro" \
  -v "$PWD/data:/data" \
  ghcr.io/ni-c/mcp-hub:0.10.0

Point a reverse proxy with a TLS certificate at 127.0.0.1:7690, then add https://mcp.example.net/hub as a custom connector in your MCP client — ChatGPT, Claude, Le Chat, Cursor and friends — and log in once with the password.

Full walkthrough →

Where to go next ​

If you want to…Read
understand the problem it solvesWhat is mcp-hub?
get a working deploymentGetting started · Deployment
write your mcp.jsonConfiguration
connect ChatGPT, Claude, Cursor, an API…Connecting clients · Client compatibility
know how clients get a client_idClient registration
reach an upstream that needs OAuthUpstreams that speak OAuth
check which standard is implementedStandards
watch a server for changesSubscriptions
run servers only when they are usedOn-demand servers
isolate a server you do not trustSandboxing
run it safely on the public internetSecurity
manage clients, tokens and upstreamsAdmin CLI
know what happens insideArchitecture
fix something that is misbehavingFAQ & troubleshooting

Released under the MIT License. Not affiliated with Anthropic; “Claude” is a trademark of Anthropic PBC.