mcp-gateway
Allows connecting a GitHub MCP backend via OAuth and exposing its tools under a github_ namespace through the gateway.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-gatewayUse the GitHub backend to list my open pull requests."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MCP Gateway
A lightweight, self-hosted MCP aggregator gateway: one public MCP endpoint in front of any number of protected backend MCP servers, with a spec-compliant OAuth 2.1 authorization server facing the MCP client — the piece most existing gateways are missing.
Claude Code / Claude.ai ──OAuth 2.1 (DCR/CIMD + PKCE)──▶ MCP Gateway ──own credentials──▶ GitHub MCP
│ ▶ Microsoft Learn MCP
└── /mcp (Streamable HTTP) ▶ …more backendsBuilt with FastAPI + FastMCP, configured by a single YAML file, stores its state in a single encrypted SQLite database, and ships as one small standalone container — no reverse proxy required, though you can put one in front of it for TLS.
Features
Client-facing (MCP authorization spec, 2025-11-25):
OAuth 2.1 authorization code flow with mandatory PKCE (S256)
Dynamic Client Registration (RFC 7591) at
/register—claude mcp addworks with no pre-shared credentialsClient ID Metadata Documents (CIMD) — HTTPS URLs as client IDs, including
private_key_jwtclient authentication, advertised viaclient_id_metadata_document_supported: trueAuthorization Server Metadata (RFC 8414) + OIDC discovery alias
Protected Resource Metadata (RFC 9728); 401 responses carry
WWW-Authenticate: Bearer resource_metadata="…"as Claude's connector requiresResource indicators (RFC 8707) accepted and bound to issued tokens
Short-lived opaque access tokens, rotating refresh tokens, single-use authorization codes — all stored hashed; client records encrypted at rest
Loopback redirect URIs match port-agnostically (Claude Code CLI registers one port and authorizes with another); non-loopback URIs require exact registration
Small Svelte 5 login + consent UI (single local identity from the config file)
Backend-facing:
none— public servers (e.g. Microsoft Learn MCP)bearer— static token injection (Authorization: Bearer …, e.g. PATs)headers— arbitrary static headers (API keys)oauth— full OAuth client per the MCP spec: metadata discovery, CIMD when the upstream AS supports it (the gateway hosts its own client metadata document), DCR fallback, PKCE, automatic token refresh. Connected once via the browser; tokens persisted encrypted (Fernet) in SQLite.The client's gateway token is never forwarded upstream (no token passthrough, as the spec demands); backends only ever see credentials the gateway holds.
Aggregation:
Tools/resources/prompts namespaced per backend:
github_create_issue,msdocs_microsoft_docs_search, …Live proxying over Streamable HTTP; a down or not-yet-connected backend only removes its own tools instead of breaking the gateway
Built-in
gateway_statustool
Related MCP server: MCP OAuth Test
Quick start
cp config.example.yaml config.yaml
$EDITOR config.yaml # set public_url, users, backends
cp .env.example .env
$EDITOR .env # set MCP_GATEWAY_ENCRYPTION_KEY (openssl rand -base64 32)
docker compose up -dThe gateway runs standalone and listens on :8000; docker compose picks up
MCP_GATEWAY_ENCRYPTION_KEY from .env automatically. Put it behind a reverse proxy of
your choice for TLS, or expose the port directly.
Generate a password hash for the config file:
docker compose run --rm mcp-gateway mcp-gateway hash-passwordConnect Claude Code (CLI)
claude mcp add --transport http gateway https://mcp.example.com/mcpClaude Code discovers the gateway's authorization server, registers itself via DCR (or
uses its CIMD client ID), and opens your browser: log in with a user from
config.yaml, approve, done. No tokens to paste.
Connect Claude.ai / Claude Code web (custom connector)
Add https://mcp.example.com/mcp as a custom connector. The browser redirect to
https://claude.ai/api/mcp/auth_callback goes through the same login/consent flow.
Connect OAuth backends
Open https://mcp.example.com/ui/backends, sign in, and press Connect next to each
OAuth backend (e.g. GitHub MCP). You'll be redirected to the backend's authorization
server once; afterwards the gateway refreshes tokens automatically.
Configuration
Everything lives in one YAML file (see config.example.yaml).
Values support ${ENV_VAR} / ${ENV_VAR:-default} expansion.
server:
public_url: https://mcp.example.com # behind your reverse proxy
auth:
encryption_key: ${MCP_GATEWAY_ENCRYPTION_KEY} # encrypts secrets at rest
users:
- username: admin
password_hash: "$2b$12$…" # mcp-gateway hash-password
access_token_expiry_seconds: 3600
refresh_token_expiry_seconds: 2592000
storage:
path: /data/gateway.db # SQLite; the only state
backends:
github: # → tools namespaced github_*
url: https://api.githubcopilot.com/mcp/
auth:
type: oauth
# GitHub's authorization server supports neither CIMD nor DCR, so
# register a GitHub OAuth App and provide its credentials directly:
client_id: ${GITHUB_OAUTH_CLIENT_ID}
client_secret: ${GITHUB_OAUTH_CLIENT_SECRET}
microsoft-docs: # → tools namespaced microsoft-docs_*
url: https://learn.microsoft.com/api/mcp
auth: { type: none }
something-with-a-pat:
url: https://example.com/mcp
auth: { type: bearer, token: "${SOME_PAT}" }Adding a backend is config-only — no code changes.
Backend auth reference
type | fields | behaviour |
| – | no credentials sent |
|
|
|
|
| static headers (API keys etc.) |
|
| full OAuth client: CIMD → DCR fallback, PKCE, refresh, encrypted store |
For oauth backends the gateway hosts its own Client ID Metadata Document at
<public_url>/oauth/client-metadata.json and uses it as its client ID whenever the
upstream AS advertises CIMD support (requires an HTTPS public_url); otherwise it
falls back to Dynamic Client Registration. If the upstream AS supports neither (e.g.
GitHub's), set client_id (and client_secret, if the app is confidential) to use a
pre-registered OAuth client instead — CIMD/DCR are skipped entirely.
Logging
The gateway logs to stdout/stderr (docker logs, docker compose logs -f), at INFO by
default: startup/shutdown, config summary, login attempts, OAuth authorize/consent/token
issuance, upstream backend connect/disconnect, and backend mount status. DEBUG adds
finer-grained detail (client construction, token rotation, CIMD refreshes, storage
housekeeping). No credentials or tokens are ever logged, at any level.
Set the level via the MCP_GATEWAY_LOG_LEVEL environment variable (debug, info,
warning, error, or critical):
# .env (picked up by docker compose)
MCP_GATEWAY_LOG_LEVEL=debug# or inline
docker compose run --rm -e MCP_GATEWAY_LOG_LEVEL=debug mcp-gatewaydocker-compose.yml already forwards this variable to the container, defaulting to
info when unset.
Outside Docker, --log-level on mcp-gateway run works the same way and takes
precedence over the env var:
mcp-gateway run -c config.yaml --log-level debugEndpoints
Path | Purpose |
| MCP endpoint (Streamable HTTP) |
| RFC 9728 protected resource metadata |
| RFC 8414 AS metadata (+ OIDC alias) |
| OAuth 2.1 endpoints (PKCE, DCR, revocation) |
| login + consent (Svelte 5) |
| backend connection status / connect / disconnect |
| the gateway's own CIMD document (upstream leg) |
| upstream OAuth connect flow |
| liveness |
Security notes
PKCE (S256) is mandatory; authorization codes are single-use and expire in 5 min.
Refresh tokens rotate on every use (OAuth 2.1 public-client requirement).
Access/refresh tokens and auth codes are stored as SHA-256 hashes only.
Registered client records and upstream credentials are Fernet-encrypted at rest (
auth.encryption_key; passphrases are stretched with scrypt + per-DB salt).The consent screen names the client and the exact redirect target, and warns on loopback redirects (CIMD localhost-impersonation guidance from the spec).
Tokens issued to MCP clients are never forwarded to backends, and backend credentials never reach MCP clients.
Sessions are signed (
itsdangerous),HttpOnly,SameSite=Lax,Secureon HTTPS.No credentials are logged.
Development
uv venv && uv pip install -e ".[dev]" # or: pip install -e ".[dev]"
(cd ui && npm install && npm run build) # build the Svelte UI
pytest # 35 tests incl. full e2e OAuth flows
mcp-gateway run -c config.yamlThe test suite spins up real gateways (and a second instance acting as an OAuth-protected upstream) and drives complete DCR/CIMD + PKCE flows over HTTP.
Architecture
src/mcp_gateway/oauth_server.py— the client-facing OAuth AS. Builds on the MCP SDK's authorization-server handlers and FastMCP's CIMD manager rather than hand-rolling protocol code; the gateway adds SQLite persistence, the login/consent transaction flow, and token issuance/rotation policy.src/mcp_gateway/upstream.py— backend clients. OAuth backends use the official SDKOAuthClientProvider(discovery, CIMD/DCR, refresh) with encrypted SQLite token storage and a browser-driven connect flow.src/mcp_gateway/gateway.py— FastMCP server; each backend is mounted as a live proxy under its namespace.src/mcp_gateway/app.py/web.py— FastAPI app: JSON API for the UI, upstream callback, CIMD document, static Svelte app; the FastMCP app (MCP endpoint + OAuth routes + well-known) is mounted at the root.ui/— Svelte 5 + Vite SPA (login, consent, backends).
Single-instance by design (SQLite + in-memory connect flows). Runs standalone; put it behind a reverse proxy of your own if you want TLS termination, and back up one file.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityNot gradedmaintenanceAggregates multiple MCP servers behind a single, secure endpoint with unified tool/resource discovery, OAuth authentication, and resilient request routing. Enables users to manage and interact with multiple MCP backends through one centralized interface with load balancing and circuit breakers.2
- FlicenseNot gradedqualityBmaintenanceMulti-tenant MCP server with OAuth 2.1 authorization, enabling tenant-scoped tool access and audit logging.
- AlicenseAqualityCmaintenanceA federated MCP gateway that consolidates multiple plain-HTTP backends into a single, OAuth-protected MCP server, enabling agents to access diverse tools through one endpoint with centralized authentication and audit.510MIT
- AlicenseNot gradedqualityCmaintenanceAuthenticating reverse proxy for MCP servers providing credential isolation, OAuth2 token management, and composite tool aggregation.BSD Zero Clause
Related MCP Connectors
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
MCP Hub: AI service discovery, per-user OAuth, and multi-service workflow orchestration
An authenticated remote MCP server for user-owned devices and one-shot capability invocation.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/R0Wi/mcp-gateway'
If you have feedback or need assistance with the MCP directory API, please join our Discord server