跳到正文
GitHub·· 9 天前AI 评分62

mcp-gateway 发布 v7.0:为 MCP 服务器提供统一网关

HarrisonCN/mcp-gateway

AI 导读

HarrisonCN/mcp-gateway 是一个位于 AI 客户端与 MCP 服务器之间的网关,提供统一 API 端点、MCP Streamable HTTP 端点、鉴权、限流、健康检查与 Prometheus 指标。

正文

Live demo: try the dashboard with simulated traffic — https://harrisoncn.github.io/mcp-gateway/ (runs entirely in your browser).

The Problem

As MCP becomes the standard protocol for AI agents to interact with tools, teams are running dozens of MCP servers — filesystem, GitHub, databases, Slack, search, and more. Managing them is chaos:

  • Every AI client connects to every server independently
  • No central authentication or access control
  • No visibility into which tools are being called, by whom, and how often
  • No rate limiting to prevent runaway agents from hammering your APIs

mcp-gateway solves this. It sits between your AI clients and your MCP servers, acting as a single, observable, secure entry point.

┌─────────────────────────────────────────────────────────┐
│                      AI Clients                         │
│   Claude Code · Cursor · Copilot · Your App · Scripts   │
└─────────────────────┬───────────────────────────────────┘
                      │  HTTP / REST
                      ▼
┌─────────────────────────────────────────────────────────┐
│                   mcp-gateway                           │
│                                                         │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────────┐  │
│  │   Auth   │  │  Router  │  │  Metrics / Monitor   │  │
│  │ API Key  │  │ Tool →   │  │  Prometheus · Logs   │  │
│  │   JWT    │  │ Server   │  │  Dashboard           │  │
│  └──────────┘  └──────────┘  └──────────────────────┘  │
│                                                         │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐              │
│  │Rate Limit│  │ Registry │  │  Health  │              │
│  └──────────┘  └──────────┘  └──────────┘              │
└──────┬──────────────┬──────────────┬────────────────────┘
       │              │              │  stdio / SSE / WS
       ▼              ▼              ▼
┌──────────┐  ┌──────────┐  ┌──────────┐
│Filesystem│  │  GitHub  │  │PostgreSQL│  ... more
│  Server  │  │  Server  │  │  Server  │
└──────────┘  └──────────┘  └──────────┘

Features

  • Unified API endpoint — one URL for all your MCP tools, auto-routed by tool name
  • MCP endpoint for clients — /mcp speaks MCP Streamable HTTP (2025-06-18 / 2025-03-26), so Claude Code, Cursor or any MCP client sees every upstream tool through one server, with the same auth, limits and metrics — including progress notifications, cancellation, logging, completions and resource subscriptions
  • Every MCP transport — stdio, streamable-http (current spec), legacy sse (HTTP+SSE) and websocket upstream servers, with per-server headers for upstream auth
  • Automatic reconnect — crashed or disconnected servers are reconnected with exponential backoff + jitter; state is visible in /servers, /health, the dashboard and Prometheus
  • Authentication — API keys (constant-time compare, storable as sha256: digests, with expiry), JWT (HMAC secret, PEM public key or JWKS URL; issuer / audience / exp checks), or no-auth; misconfiguration fails closed
  • Hardening — security headers + hash-based CSP, IP allowlist, Host / Origin checks against DNS rebinding, body and argument size limits, brute-force lockout, secret redaction in logs and history, startup security warnings (mcp-gateway validate --strict)
  • Rate limiting — per-key sliding-window counter, with standard X-RateLimit-* headers
  • Per-key scopes — restrict an API key (or a JWT via claims) to some servers / tools and give it its own rate limit; enforced on REST and /mcp
  • Concurrency limits — per-server maxConcurrency, queued requests count against timeout
  • Health monitoring — periodic MCP ping health checks with latency (every 30 s, configurable)
  • Metrics — Prometheus-compatible /metrics endpoint (monotonic counters) + JSON aggregation
  • Config hot reload — servers, API keys / auth, rate limits, CORS and reconnect policy apply without a restart (disable with --no-watch)
  • Optional auth for health & metrics — keep /health and /metrics public (default) or put them behind auth; the dashboard asks for a key
  • Tool discovery — GET /api/v1/tools lists all tools across all servers
  • Resources & prompts — resources/* and prompts/* from every server, aggregated on REST and /mcp
  • Persistent audit log — optional SQLite history (built-in node:sqlite, no dependency) queryable via GET /api/v1/requests and the dashboard
  • Tool filtering — per-server tools.allow / tools.deny glob patterns hide tools you don't want exposed (and block calls to them)
  • YAML/JSON config — simple, declarative configuration with env var overrides
  • Docker-ready — official Docker image, Compose examples included
  • TypeScript SDK — embed the gateway as a library in your own project
  • Client libraries — a dependency-free TypeScript client (clients/js, browser + Node) a Kotlin/JVM/Android client (clients/kotlin), and Python, Go and Swift SDKs (clients/python, clients/go, clients/swift)
  • LLM tool schemas — GET /api/v1/tools?format=openai|anthropic returns ready-to-use function-calling definitions

Quick Start

Install

npm install -g @winstonsayno/mcp-gateway
# or
npx @winstonsayno/mcp-gateway init

Configure

# Generate a default config file
mcp-gateway init

# Edit mcp-gateway.yml to add your servers
# mcp-gateway.yml
port: 4000

servers:
  - id: filesystem
    name: Filesystem
    transport: stdio
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]

  - id: github
    name: GitHub
    transport: stdio
    command: npx
    args: ["-y", "@modelcontextprotocol/server-github"]
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}"

Run

mcp-gateway start
# → mcp-gateway listening on http://0.0.0.0:4000
# → ✓ Filesystem — 8 tools available
# → ✓ GitHub — 26 tools available

Call a Tool

# List all available tools
curl http://localhost:4000/api/v1/tools

# Call a tool (auto-routes to the right server)
curl -X POST http://localhost:4000/api/v1/tools/call \
  -H "Content-Type: application/json" \
  -d '{"tool": "read_file", "arguments": {"path": "/tmp/hello.txt"}}'

# With authentication
curl -X POST http://localhost:4000/api/v1/tools/call \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"tool": "create_issue", "server": "github", "arguments": {"title": "Bug report", "body": "..."}}'

Use the gateway as an MCP server (/mcp)

The gateway is itself an MCP server: http://<host>:4000/mcp implements the Streamable HTTP transport (protocol 2025-06-18, 2025-03-26 accepted). Clients get one aggregated, filtered tool list; calls are routed to the right upstream server with the gateway's auth, rate limit, maxConcurrency, timeouts, metrics and request log.

Claude Code

claude mcp add --transport http gateway http://localhost:4000/mcp \
  --header "Authorization: Bearer your-secret-key"

Cursor (~/.cursor/mcp.json or .cursor/mcp.json)

{
  "mcpServers": {
    "gateway": {
      "url": "http://localhost:4000/mcp",
      "headers": { "Authorization": "Bearer your-secret-key" }
    }
  }
}

Clients that only speak stdio (e.g. older Claude Desktop builds) can bridge with mcp-remote: npx mcp-remote http://localhost:4000/mcp --header "Authorization: Bearer your-secret-key".

Any SDK client

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const client = new Client({ name: 'my-app', version: '1.0.0' });
await client.connect(new StreamableHTTPClientTransport(new URL('http://localhost:4000/mcp'), {
  requestInit: { headers: { Authorization: 'Bearer your-secret-key' } },
}));
const { tools } = await client.listTools();

What the endpoint does:

POST /mcp JSON-RPC: initialize, ping, tools/list (paginated), tools/call, resources/list, resources/templates/list, resources/read, resources/subscribe / unsubscribe, prompts/list, prompts/get, logging/setLevel, completion/complete, notifications (incl. notifications/cancelled). Batches are accepted. Responses are application/json; a single tools/call with _meta.progressToken switches to an SSE reply when the upstream reports progress (notifications/progress, then the result).
GET /mcp SSE stream for server→client notifications: notifications/tools/list_changed, notifications/resources/list_changed and notifications/prompts/list_changed are sent when the aggregated list a session sees changes (a server announces changes, connects, is removed by hot reload, …); notifications/resources/updated for subscribed URIs; upstream notifications/message at or above the session's logging/setLevel level (logger = <serverId>/<logger>).
Resources & prompts Resource URIs are passed through unchanged; when two servers list the same URI the lowest server id wins. resources/read is routed by exact URI, then by resource template, then to the only server with resources. Prompt names follow toolNaming like tools. resources/subscribe is routed the same way; sessions share one upstream subscription per URI, restored after reconnects. completion/complete is routed by prompt name or resource template.
DELETE /mcp Ends the session.
Sessions initialize returns Mcp-Session-Id; later requests must send it (400 if missing, 404 if unknown or expired). A session is bound to the API key / JWT subject that created it. Idle sessions expire after mcp.sessionIdleTimeoutSeconds.
Tool names toolNaming: auto (default) keeps a tool's name unless two servers expose the same name; then every copy becomes <serverId>__<tool>. prefix always uses <serverId>__<tool>. Ordering is deterministic (server id, then tool name). In auto mode the prefixed form is also accepted by tools/call.
Errors Unknown tool / bad params → JSON-RPC -32602; rate limit → -32029 with data.retryAfter; server offline or timed out → a normal result with isError: true (so the model sees it); upstream JSON-RPC errors are forwarded unchanged; cancelled → -32800.
Cancellation notifications/cancelled (or the client dropping the HTTP request) cancels the upstream call, which receives its own notifications/cancelled.
Security Same auth as the REST API (Authorization: Bearer … or X-API-Key). Requests with an Origin header are rejected (403) unless it matches mcp.allowedOrigins (default: cors.origins) — set this when the gateway listens on a reachable address.

API Reference

Method Path Description
GET /api/v1/health Gateway health and server summary
GET /api/v1/servers List all registered servers
GET /api/v1/servers/:id Get server details and tools
GET /api/v1/tools List all tools (filterable by ?server= or ?tag=; ?format=openai|openai-responses|anthropic for LLM schemas)
POST /api/v1/tools/call Invoke a tool
POST /api/v1/servers/:id/reconnect Reconnect a server now (resets backoff)
GET /api/v1/health/live Liveness probe — always public, returns only {"status":"ok"}
GET /api/v1/health/ready Readiness probe — always public; 200 when servers are ready, else 503 (?min=N)
GET /api/v1/metrics Aggregated metrics (JSON or Prometheus)
GET /api/v1/resources Resources of all servers (?server=), duplicate URIs collapsed
GET /api/v1/resources/templates Resource templates (?server=)
POST /api/v1/resources/read Read a resource: {"uri": "...", "server"?: "..."}
GET /api/v1/prompts Prompts of all servers (?server=)
POST /api/v1/prompts/get Get a prompt: {"name": "...", "server"?: "...", "arguments"?: {...}}
GET /api/v1/requests Request history, newest first (?limit= max 500, server, tool, client, success, via, kind, since, until, cursor)
GET /api/v1/stats Live dashboard data: time series (count, errors, p50/p95 per bucket), summary, top tools, per-server and per-key usage (?window=, ?bucket= ms)
GET /api/v1/events Server-Sent Events: a request event per call, a snapshot (health + summary) every 2 s
POST GET DELETE /mcp MCP Streamable HTTP endpoint (see above)

/health and /metrics are unauthenticated by default; set auth.protect.health / auth.protect.metrics to require auth for them too (/health/live and /health/ready always stay public for Docker / Kubernetes probes). Every other route requires auth when it is enabled. /metrics returns JSON by default (?window=<ms>); with monitor.prometheus: true it returns the Prometheus text format when the client asks for text/plain (as Prometheus does) or passes ?format=prometheus.

LLM tool schemas

GET /api/v1/tools?format=openai (Chat Completions), openai-responses (Responses API) or anthropic (Messages API) returns the tools the caller may use as function-calling definitions, plus a mapping from each LLM tool name back to the gateway server and tool:

{
  "format": "anthropic",
  "tools": [{ "name": "github__create_issue", "description": "…", "input_schema": { "type": "object", "properties": { … } } }],
  "mapping": { "github__create_issue": { "server": "github", "tool": "create_issue" } },
  "total": 1
}

Pass tools straight to the provider; when the model calls a tool, look it up in mapping and POST /api/v1/tools/call with that server / tool (the clients' callLlmTool() does this). Names follow mcp.toolNaming, are sanitised to ^[a-zA-Z0-9_-]{1,64}$ and de-duplicated; $schema is stripped and parameters is always an object schema. Scopes and ?server= / ?tag= filters apply.

Client libraries

TypeScript / JavaScript — clients/js @winstonsayno/mcp-gateway-client: zero dependencies, fetch-based (browser, Node 18+, Deno, Bun, React Native), typed health, servers, listTools, toolSchemas, callTool, callLlmTool, plus a small MCP-over-/mcp session helper
Kotlin / JVM / Android — clients/kotlin OkHttp + kotlinx.serialization, Java 11 bytecode; same API surface, McpSession for /mcp
Python — clients/python mcp-gateway-client: stdlib only, Python ≥ 3.9, typed health, list_tools, tool_schemas, call_tool, call_llm_tool, approvals, GatewayError.is_policy_error
Go — clients/go github.com/HarrisonCN/mcp-gateway/clients/go: net/http only, Go ≥ 1.21, context-aware, *mcpgateway.Error with IsPolicyError()
Swift — clients/swift SwiftPM MCPGateway: async/await, URLSession (pluggable transport), macOS 12 / iOS 15 / Linux

Both are in this repository and not yet published to npm / Maven Central.

POST /api/v1/tools/call responses:

Status Meaning
200 Tool returned a result
400 Invalid body (tool must be a string, arguments an object) or malformed JSON
403 The tool is hidden by the server's tools filter (also when "server" is passed explicitly), or outside the caller's scope
404 Unknown tool or server
409 Tool name is exposed by several servers — pass "server" to choose
429 Rate limited (see Retry-After)
502 The MCP server returned an error
503 Server is not connected (body has status, e.g. reconnecting; Retry-After when a retry is scheduled)
504 The MCP server did not answer within timeout

Configuration Reference

port: 4000                    # HTTP port (env: MCP_GATEWAY_PORT)
host: 0.0.0.0                 # Bind address (env: MCP_GATEWAY_HOST)
logLevel: info                # debug | info | warn | error

auth:
  strategy: api-key           # none | api-key | jwt | oauth2 (OAuth 2.1 resource server, see docs/configuration.md)
  apiKeys:
    - "your-secret-key"       # full access
    - "sha256:…"              # a key stored as its digest (mcp-gateway gen-key / hash-key)
    - key: "${APP_KEY}"       # scoped key (see "Per-key scopes")
      name: app
      expiresAt: "2027-01-01" # optional expiry; disabled: true switches a key off
      scope:
        servers: ["github"]
        tools: ["read_*"]
        rateLimit: { limit: 30, windowSeconds: 60 }
  protect:
    health: false             # true → /api/v1/health requires auth
    metrics: false            # true → /api/v1/metrics requires auth (configure your scraper)

reconnect:                    # automatic reconnect of crashed / disconnected servers
  enabled: true
  initialDelayMs: 1000        # first retry delay
  maxDelayMs: 60000           # backoff cap
  multiplier: 2               # delay *= multiplier after each failure
  jitter: 0.2                 # ±20 % randomisation
  maxAttempts: 0              # 0 = retry forever; else give up (status: offline)

health:
  intervalMs: 30000           # MCP ping interval
controlPlane:
  dashboard: true             # serve /dashboard

rateLimit:
  limit: 100                  # Max requests per window
  windowSeconds: 60           # Window duration
  perKey: true                # Per-key or global

monitor:
  requestLog: true            # Log all requests
  prometheus: true            # Enable Prometheus /metrics
  retentionHours: 24          # Metrics retention

security:                     # hardening (see docs/configuration.md#security)
  authLockout: true           # 429 for IPs with repeated auth failures
  dnsRebindingProtection: false  # true for a local gateway without auth
  ipAllowlist: ["10.0.0.0/8"]
  maxToolArgumentsBytes: 262144

cors:
  origins:
    - "https://your-app.com"

audit:                        # persistent request history (SQLite, Node 22.5+; default off)
  enabled: false
  path: mcp-gateway-audit.db
  retentionDays: 30           # 0 = keep forever

mcp:                          # downstream MCP endpoint (Streamable HTTP)
  enabled: true               # restart required to change
  path: /mcp                  # restart required to change; not "/" or under /api, /dashboard
  toolNaming: auto            # auto | prefix  ("<serverId>__<tool>")
  pageSize: 500               # tools per tools/list page
  sessionIdleTimeoutSeconds: 1800
  maxSessions: 1000           # least recently used idle session is evicted beyond this
  # allowedOrigins: ["https://your-app.com"]   # browser origins allowed on /mcp (default: cors.origins)
  # instructions: "Tools for the ACME workspace"  # returned from initialize

servers:
  - id: my-server             # Unique identifier
    name: My Server           # Display name
    transport: stdio          # stdio | streamable-http | sse | websocket
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
    env:
      MY_VAR: "${ENV_VAR}"    # Environment variable substitution
    tags: [files, local]
    enabled: true
    timeoutMs: 30000          # ms, includes time queued behind maxConcurrency (schema v4: timeout)
    maxConcurrency: 10        # max in-flight tool calls for this server
    tools:                    # optional: expose only some tools (globs: * and ?, deny wins)
      allow: ["read_*", "list_*"]
      deny: ["*_secret"]
    reconnect:                # optional per-server override of the reconnect block
      maxAttempts: 5

  - id: remote
    name: Remote server
    transport: streamable-http   # MCP 2025-03-26+ (POST + optional SSE responses, Mcp-Session-Id)
    url: https://mcp.example.com/mcp
    headers:
      Authorization: "Bearer ${REMOTE_MCP_TOKEN}"   # ${VAR} expanded from the gateway's env

  - id: legacy
    name: Legacy SSE server
    transport: sse            # MCP 2024-11-05 HTTP+SSE (GET stream + POST to the announced endpoint)
    url: http://localhost:8080/sse

  - id: socket
    name: WebSocket server
    transport: websocket      # one JSON-RPC message per frame, "mcp" subprotocol
    url: ws://localhost:8081
    subprotocol: mcp          # "" to request no subprotocol

Hot reload

With mcp-gateway start the config file is watched (disable with --no-watch). On save:

Applied immediately Needs a restart
servers (added / changed / removed / disabled) port, host
auth (strategy, keys, JWT secret, protect) monitor.retentionHours
rateLimit (counters reset when it changes) health.intervalMs
cors.origins, monitor.requestLog, monitor.prometheus dashboard
reconnect, logLevel mcp.enabled, mcp.path, audit
mcp.toolNaming, mcp.pageSize, session limits, mcp.allowedOrigins

An invalid file is rejected and the running config is kept. MCP_GATEWAY_* env overrides keep precedence.

Per-key scopes

Give each app its own key and only the tools it needs. Plain string keys keep full access; object entries can be restricted (all fields except key are optional):

auth:
  strategy: api-key
  apiKeys:
    - "admin-key"                       # unrestricted
    - key: ${AURA_GATEWAY_KEY}          # ${VAR} is expanded in object keys
      name: aura                        # client id "key:aura" in logs, metrics and sessions (unique)
      scope:
        servers: ["github", "fs-*"]       # server id globs
        tools: ["read_*", "github/create_issue"]   # tool globs; "server/tool" when the pattern has a "/"
        rateLimit: { limit: 30, windowSeconds: 60 } # own bucket instead of the global rateLimit
  • A tool must pass the server's own tools filter and the key's servers and tools lists. An absent list means no restriction; an empty list allows nothing.
  • Discovery hides what a key may not use: GET /tools, GET /servers, GET /servers/:id (→ 404), /mcp tools/list.
  • Calls are refused: POST /tools/call and POST /servers/:id/reconnect → 403; /mcp tools/call → JSON-RPC error -32003. Auto-routing only considers servers the key may use, so a name that collides elsewhere can still be called without "server".
  • On /mcp, collision prefixes are computed from what the key can see (a key scoped to one server sees bare names).
  • Restricted keys only see their own entries in GET /requests.
  • JWT: put globs in the mcp_servers / mcp_tools claims (array, or a space/comma-separated string), e.g. {"sub": "user-1", "mcp_servers": ["github"], "mcp_tools": "read_* github/create_issue"}. A malformed claim allows nothing.
  • Hot reloadable: changing scopes applies to the next request; open /mcp sessions get notifications/tools/list_changed, and sessions of removed keys are closed.

Resources & prompts

Servers that announce the resources / prompts capabilities have their resources, resource templates and prompts listed at connect time (and refreshed on notifications/*/list_changed). They are available on REST (/api/v1/resources, /resources/templates, /resources/read, /prompts, /prompts/get) and on /mcp. Reads and gets are forwarded live with the server's timeout, counted against the rate limit and recorded in metrics / history with kind: "resource" or "prompt". Key scopes apply by server (servers globs); tools globs and servers[].tools filters only concern tools.

curl -s localhost:4000/api/v1/resources
curl -s -X POST localhost:4000/api/v1/resources/read -H 'content-type: application/json' -d '{"uri":"file:///notes/todo.md"}'
curl -s -X POST localhost:4000/api/v1/prompts/get -H 'content-type: application/json' \
  -d '{"name":"review-code","server":"github","arguments":{"pr":"42"}}'

Persistent audit log

By default request history lives in memory (monitor.retentionHours). Enable the audit log to keep it in SQLite across restarts:

audit:
  enabled: true
  path: ./data/mcp-gateway-audit.db   # default mcp-gateway-audit.db (WAL mode)
  retentionDays: 30                   # pruned hourly; 0 = keep forever
  • Uses Node's built-in node:sqlite (Node 22.5+): no extra dependency and nothing native to compile. Node 22.0–22.4 refuses to start with audit.enabled: true and says why. Node may print an ExperimentalWarning for node:sqlite.
  • Only metadata is stored: time, server, tool / URI / prompt, kind, duration, success, error message, client id, via (rest / mcp). Arguments and results are never stored.
  • GET /api/v1/requests then reads from the database ("source": "audit") and supports filters (server, tool, client, success=true|false, via=rest|mcp, kind=tool|resource|prompt, since / until as ISO or epoch ms) and paging (nextCursor → ?cursor=). Restricted keys only ever see their own records. The dashboard's Request History panel has the same filters and a Load older button.
  • Library users can plug in any store: metrics.setAuditStore(myStore) with the AuditStore interface.
  • Changing audit requires a restart.

Tool filtering

Expose only part of a server's tools — e.g. make a filesystem server read-only, or drop tools that clash with another server's names:

servers:
  - id: filesystem
    name: Filesystem (read-only)
    transport: stdio
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"]
    tools:
      allow: ["read_*", "list_*", "search_files", "get_file_info"]
      deny: ["*_media_file"]
  • Patterns are globs matched against the whole tool name, case-sensitive: * = any characters, ? = one character.
  • A tool is exposed when it matches an allow pattern (or allow is absent / empty) and no deny pattern. Deny wins.
  • Hidden tools are absent from GET /tools, GET /servers/:id, tool counts and metrics, and don't cause name conflicts with other servers. Calling one returns 404 (auto-routing) or 403 (explicit "server").
  • The filter is applied to every tool list, including updates via notifications/tools/list_changed. Changing it in the config file is applied by hot reload (the server is reconnected).

Reconnect & server status

Each server's health.status is one of online, degraded (connected, but the health ping failed), reconnecting (lost — a retry is scheduled or running), offline (gave up, or reconnect disabled) or unknown. GET /api/v1/servers/:id also returns health.reconnect (state, attempt, nextAttemptAt, lastError, reconnects) and session (transport, negotiated protocolVersion, serverInfo, connectedAt).

Prometheus series added: mcp_gateway_server_up, mcp_gateway_server_status{status=…}, mcp_gateway_server_reconnects_total, mcp_gateway_server_reconnect_attempt, mcp_gateway_server_ping_ms.

Docker

# Pull and run
docker run -p 4000:4000 \
  -v $(pwd)/mcp-gateway.yml:/app/mcp-gateway.yml \
  -e GITHUB_TOKEN=ghp_... \
  ghcr.io/harrisoncn/mcp-gateway:latest

# Or with Docker Compose (gateway + Prometheus; see examples/docker)
cd examples/docker
GATEWAY_API_KEY=change-me docker compose up

The image's HEALTHCHECK uses the always-public /api/v1/health/live, so it keeps working when auth.protect.health is on.

Liveness vs. readiness (Kubernetes, load balancers)

Probe Answers Use it for
GET /api/v1/health/live always 200 {"status":"ok"} while the process serves HTTP restart a hung container
GET /api/v1/health/ready 200 when the upstream servers are ready, otherwise 503 only route traffic to gateways that can serve tool calls

A server counts as ready when it is enabled, connected and not degraded (failing health pings). By default every enabled server must be ready; ?min=N requires at least N instead (useful when some servers are optional). With no servers configured the gateway is ready. While shutting down it answers 503 {"status":"shutting_down"} so load balancers drain it first. The body only carries counts:

{ "status": "not_ready", "servers": { "ready": 1, "total": 2, "required": 2 } }
# Kubernetes
livenessProbe:
  httpGet: { path: /api/v1/health/live, port: 4000 }
readinessProbe:
  httpGet: { path: /api/v1/health/ready, port: 4000 }   # or /api/v1/health/ready?min=1
  periodSeconds: 10

Dashboard

Open http://localhost:4000/dashboard. The first visit opens a short guided setup: connect with an API key, see the upstream servers, call a tool from a form generated from its JSON schema, and copy a ready-made config for Claude Desktop, Cursor, Claude Code, the JS / Kotlin clients or curl. Reopen it any time with the ? button. After that the dashboard shows live request rate, p50 / p95 latency, error rate, top tools, usage per key, a live request stream, server health (with reconnect) and the filterable request history. It is one static file with no build step and no CDN; English / 中文, dark / light, and it works on phones.

Dashboard overview

When auth is enabled the key is kept in the browser tab (sessionStorage, or localStorage with “remember”) and sent as Authorization: Bearer … on every API call. The page itself contains no data; set dashboard.enabled: false to stop serving it. See dashboard/README.md.

Embed as a Library

import { Gateway, loadConfig } from '@winstonsayno/mcp-gateway';

const config = await loadConfig('./mcp-gateway.yml');
const gateway = new Gateway(config);

await gateway.start();
// Gateway is now running at http://localhost:4000

// Graceful shutdown
process.on('SIGTERM', () => gateway.stop());

What's New in v7.0

⚠ Breaking release — read Migrating to 7.0. On 6.9: npx @winstonsayno/mcp-gateway@6.9 migrate --write.

Change What to do
Config schema v7 version: 7 only — mcp-gateway migrate --to 7 (6.9) rewrites your file
admin / dashboard → controlPlane Moved by migrate --to 7
Control plane / data plane controlPlane.role: control on one gateway, role: data + url + token on the rest — guide

What's New in v6.9

Feature Description
migrate --to 7 Moves admin / dashboard under controlPlane, keeps comments
Deprecations Schema v6, admin, dashboard — all removed in 7.0

What's New in v6.8

Feature Description
Helm chart Hardened Deployment, HPA, PDB, ServiceMonitor, optional operator
Operator McpGateway resources reconciled with server-side apply and status

What's New in v6.7

Feature Description
Usage metering Calls, tokens and duration per tenant, per month, per tool
Invoices Price book, discounts, minimums, tax; JSON or CSV

What's New in v6.6

Feature Description
Abuse detection Bursts vs baseline, error spikes, tool enumeration; optional quarantine
Injection scoring Weighted prompt-injection signals on arguments and results

What's New in v6.5

Feature Description
Policy simulation Replay history against a candidate policy; see who and what would be denied
Shadow policies Evaluate a second policy on live traffic without enforcing it

What's New in v6.4

Feature Description
SCIM 2.0 Okta / Entra provision users and groups (filters, PATCH, group members)
OIDC SSO ID-token verification, PKCE login URL, IdP groups → tenant roles

What's New in v6.3

Feature Description
GenAI semconv execute_tool / chat spans with gen_ai.* attributes and token usage
GenAI metrics Operation duration and token usage histograms, OTLP push or pull

What's New in v6.2

Feature Description
Workflow DAGs Nodes with dependencies run in parallel; conditions, retries, continue-on-error
Async runs Start, poll, and inspect per-node status and output

What's New in v6.1

Feature Description
GraphQL upstreams Operations become tools; input schemas from variable definitions
gRPC upstreams Unary methods over Connect / JSON transcoding

What's New in v6.0

Change What to do
Config schema v6 version: 6 only — mcp-gateway migrate --to 6 (5.9) rewrites your file
compliance.pii → dlp Converted by migrate --to 6; compliance reports read DLP
Plugin API v3 removed Declare apiVersion: 4
Node.js 22+ Enforced by the CLI

What's New in v5.9

Getting ready for 6.0: npx @winstonsayno/mcp-gateway@5.9 migrate --write — see Migrating to 6.0.

Feature Description
Schema v6 preview version: 6 accepted; init and examples write it
migrate --to 6 Converts compliance.pii to dlp, keeps comments
Deprecations Schema v5, compliance.pii, plugin API v3 — all removed in 6.0

What's New in v5.8

Feature Description
Adaptive pools Interchangeable servers / models scored by quality, cost and latency
Learns online Latency and errors from traffic, quality from feedback; Thompson-sampling exploration

What's New in v5.7

Feature Description
Python SDK stream_tool() and McpSession for the gateway's /mcp
Go SDK StreamTool() and Client.MCP() sessions
Release checklist Version, build and publish steps for every SDK

What's New in v5.6

Feature Description
DLP levels PII + custom detectors classified public → restricted
Per-tenant masking Clearance per tenant; mask, redact, stable pseudonyms, or block
Call hooks registerCallHook() — your own stage in the call pipeline

What's New in v5.5

Feature Description
Session recordings Capture an agent's tool calls by client and time window; import / export JSON
Replay evals Re-run a recording graded by success, structure or exact match — regression tests for agents

What's New in v5.4

Feature Description
Signed plugins Ed25519 .sig files verified at load; pluginTrust.requireSigned refuses unsigned code
Plugin marketplace Browse signed indexes and install with sha256 + signature checks
mcp-gateway plugin keygen, sign, verify

What's New in v5.3

Feature Description
Edge fleet view Configured + seen edges with config drift (in-sync / stale / offline / …)
Config push POST /api/v1/admin/edge-fleet/push — by node, label ring, or only drifted edges
Dashboard card Edge nodes with drift badges and a Push config button

What's New in v5.2

Feature Description
Active-active regions Peer gateways replicate shared state (last-writer-wins) and gossip upstream health
Cross-region failover GET /api/v1/admin/regions/route/:serverId → local, best healthy peer, or none

What's New in v5.1

Feature Description
Conformance suite mcp-gateway conformance <url> — 11 protocol checks against any Streamable HTTP MCP endpoint
Self-test POST /api/v1/admin/conformance/run checks the running gateway's own /mcp
Feature modules GET /api/v1/admin/features; embedders add modules with registerFeature()

What's New in v5.0

⚠ Breaking release — read Migrating to 5.0. On 4.9: npx @winstonsayno/mcp-gateway@4.9 migrate --write.

Feature Description
Config schema v5 version: 5, servers[].timeoutMs — mcp-gateway migrate does it for you
Plugin API v4 ctx.state per-plugin store; v2 refused, v3 deprecated until 6.0
Removals Every 4.x deprecation is gone; errors name the replacement
Roadmap to 6.0 Multi-region clusters, edge fleet management, DLP, signed plugins, SDK publishing — see ROADMAP

What's New in v4.9

Feature Description
Schema v5 preview 4.9 reads version: 5 (servers[].timeoutMs) — migrate before upgrading
migrate --to 5 Rewrites v3 / v4 files to v5 in place, comments kept
Plugin API v4 ctx.state: per-plugin key-value store with TTLs
5.0 deprecations version: 4, servers[].timeout, plugin API v2 — warned now, removed in 5.0

What's New in v4.8

Feature Description
Edge config sync Edges pull ETag'd snapshots from a Node control plane and boot from the cached one offline
Offline queue Queue calls to unreachable upstreams (offline.queueTools), replay on the next sync
Usage outbox Edge calls flow back into the control plane's metrics; /admin/edge/nodes lists edges

What's New in v4.7

Feature Description
Python SDK clients/python — stdlib only, typed, policy-aware errors
Go SDK clients/go — net/http only, context-aware
Swift SDK clients/swift — async/await, macOS / iOS / Linux
SDK CI New sdks.yml workflow builds and tests all three

What's New in v4.6

Feature Description
Config editor New dashboard tab: forms + raw JSON for the running config
Validate · diff · apply Schema-checked, previewed, hot-reloaded — no restart, no YAML editing
Safe by default Read-only unless controlPlane.configApi: true; secrets stay redacted

What's New in v4.5

Feature Description
Upstream mTLS The gateway presents its certificate and verifies upstreams against a trust bundle
SPIFFE identities tls.spiffeId checks the workload identity instead of the hostname
Hot certificate rotation SVID files re-read on an interval; no restart

What's New in v4.4

Feature Description
POST /api/v1/tools/stream Progress, partial chunks and the result as Server-Sent Events
Backpressure Coalesced progress for slow readers, slow-consumer cut-off
maxQueue Per-server load shedding: fail fast with 503 instead of piling up

What's New in v4.3

Feature Description
Per-call LLM cost Token usage from _meta.usage × model prices, plus flat per-tool prices
Budgets Per pool / client / tenant, daily or monthly; alerts + webhook, optional hard block
GET /api/v1/costs Spend by client, tenant, server, tool or model

What's New in v4.2

Feature Description
Tool chains Declarative multi-step pipelines across servers, exposed as one MCP tool
Multi-agent fan-out forEach + concurrency, parallel groups, when, templates
Same guard rails Each step runs as the caller through scopes, policy, plugins and audit

What's New in v4.1

Feature Description
MCP 2025-11-25 Newest spec revision, negotiated per session; mcp.protocolVersions to pin
Structured output, resource links, annotations Passed through to new clients, downgraded for old ones
GET /api/v1/mcp/protocol Feature matrix + what each upstream negotiated

What's New in v4.0

Feature Description
Config schema v4 version: 4, API-key scope:, smart instead of least-latency — mcp-gateway migrate does it for you
Plugin API v3 ctx.secrets, ctx.tenant, onConfigChange; v1 refused, v2 deprecated until 5.0
Roadmap to 5.0 MCP spec updates, orchestration, cost budgets, streaming, mTLS, config editor, SDKs — see ROADMAP

⚠ Breaking release — read Migrating to 4.0.

What's New in v3.9

Feature Description
mcp-gateway migrate Rewrites your config to schema v4, comments kept; --check for CI
v4 deprecation warnings version: 3, flat API-key scope, least-latency — removed in 4.0
mcp-gateway bench Built-in load test with latency percentiles; see benchmarks

Details: CHANGELOG · Migrating to 4.0.

What's New in v3.8

Feature Description
Self-serve keys /portal: sign up, get a scoped key; open, approval or closed signup
Usage 7-day calls, errors, latency per key; rotate / revoke
Interactive docs Tools in scope with schemas, example arguments, snippets and “Try it”

Details: CHANGELOG · Configuration.

What's New in v3.7

Feature Description
PII redaction E-mail, phone, card, SSN, IBAN, IP, PRC ID — redact, block or tag, both directions
Data residency Pin tenants to regions; cross-region calls and failover are refused
SOC 2 / GDPR reports Controls with evidence from live config and history, JSON or Markdown

Details: CHANGELOG · Configuration.

What's New in v3.6

Feature Description
Peering Gateways in several regions, HMAC-signed peer requests
Catalog sync Each gateway sees its peers' servers and their health
Cross-region failover Calls to a downed local server are served by a peer; server: "id@peer" for remote-only servers

Details: CHANGELOG · Configuration.

What's New in v3.5

Feature Description
Vault / KMS secret://vault/…, secret://kms/… references in server env, headers, URL, args
Rotation Periodic or on-demand re-read; servers reconnect with new credentials
Per-tenant credentials inject: adds a tenant's own key to each call, never logged

Details: CHANGELOG · Configuration.

What's New in v3.4

Feature Description
Canary / A-B splits routing.splits: weighted, sticky-per-client traffic shares between servers
Auto rollback Variant guards on error rate / latency; reset via API
Smart balancing strategy: smart scores replicas by latency, error rate and cost

Details: CHANGELOG · Configuration.

What's New in v3.3

Feature Description
WASM plugins wasm: ./plugin.wasm — write plugins in any language that compiles to WebAssembly
Tenant isolation One sandbox per tenant / client; no WASI, no host access; time and memory limits
API GET /api/v1/plugins lists plugins and live sandboxes

Details: CHANGELOG · Plugins guide.

What's New in v3.2

Feature Description
Request debugger Click a History row: arguments, result, metadata
Replay Re-run a call with the same or edited arguments; structural diff of the results
API GET /api/v1/requests/:id, POST /api/v1/requests/:id/replay (replay.enabled)

Details: CHANGELOG.

What's New in v3.1

Feature Description
Sampling passthrough Upstream sampling/createMessage reaches the calling MCP client's LLM
Elicitation passthrough elicitation/create asks the calling client's user
Roots passthrough roots/list + roots/list_changed between clients and servers

Config: mcp.passthrough. Details: CHANGELOG.

What's New in v3.0

Breaking: config schema v3 (cors.origins, health.intervalMs), plugin API v2 — see the migration guide and the roadmap.

Feature Description
Config schema v3 corsOrigins / healthCheckIntervalMs removed; version: 3
Plugin API v2 Hook context argument, new onError hook; v1 deprecated
Docs Refreshed for v3, migrating to 3.0, roadmap 3.1 – 4.0

Details: CHANGELOG.

What's New in v2.9

Feature Description
Admin API Read, validate, diff and hot-apply the config over REST (/api/v1/admin)
Declarative config mcp-gateway diff / apply — guide
3.0 deprecations corsOrigins → cors.origins, healthCheckIntervalMs → health.intervalMs (warnings in 2.9, removed in 3.0)

Details: CHANGELOG.

What's New in v2.8

Feature Description
Policy as code policy.files + policy unit tests, mcp-gateway policy test for CI
SIEM export Audit records to syslog (UDP / TCP / TLS) or webhooks — guide

Details: CHANGELOG.

What's New in v2.7

Feature Description
OpenAI tools proxy /openai/v1/tools, /tool_calls, /chat/completions with an automatic tool loop
A2A bridge Agent Card at /.well-known/agent-card.json + JSON-RPC message/send — guide

Details: CHANGELOG.

What's New in v2.6

Feature Description
Edge gateway @winstonsayno/mcp-gateway/edge: Fetch-API gateway for remote MCP servers, no Node dependencies
Runtime adapters Cloudflare Workers, Deno, Bun (and Node) — guide

Details: CHANGELOG.

What's New in v2.5

Feature Description
Usage quotas per key or per tenant, hour / day / month, scoped by server / tool — 429 + Retry-After
Metering export GET /api/v1/usage as JSON or CSV, grouped by key / tenant / server / tool / hour / day

Details: CHANGELOG.

What's New in v2.4

Feature Description
Upstream catalog built-in reference servers + your own JSON catalogs (catalog.sources)
One-click add dashboard Add a server / POST /api/v1/catalog/:id/install, optional persistence (serversFile)

Details: CHANGELOG.

What's New in v2.3

Feature Description
Tenants tenants: workspaces owning servers, with members matched by client id
RBAC owner / admin / viewer roles enforced on REST, /mcp and approvals; dashboard Workspaces card

Details: CHANGELOG.

What's New in v2.2

Feature Description
Result caching cache.rules: per-tool opt-in TTL cache, per-client or shared, LRU
In-flight dedupe identical concurrent calls share one upstream request

Details: CHANGELOG.

What's New in v2.1

Feature Description
Load balancing replicas: per server with round-robin / weighted / least-latency / random / failover strategies
Failover retry the next healthy member on not-connected (opt-in timeout, error); passive ejection
Health checks every member is pinged; GET /api/v1/load-balancing shows members and ejections

Details: CHANGELOG.

What's New in v2.0

Feature Description
Plugins onRequest middleware, onToolCall before policy, onResponse after output filtering — guide
SIGHUP reload kill -HUP re-reads the config (plugins included), even with --no-watch
Node 22+ breaking: Node 20 support dropped — migration guide

Details: CHANGELOG.

What's New in v1.7

Feature Description
Clients 1.7 JS + Kotlin clients gain approvals() / approve() / deny() and policy-aware errors (code, isPolicyError)
Maven Central ready Kotlin client POM, sources/javadoc jars, signing, Central Portal publish workflow

Details: CHANGELOG.

What's New in v1.6

Feature Description
Tool policy policy.rules: allow / deny / approve per client, server, tool and argument (regex, glob, notUnder path sandboxing, …)
Human approval flagged calls wait for an operator: dashboard Pending approvals card, POST /api/v1/approvals/:id/approve
Output filtering prompt-injection detectors on tool results: redact, flag or block

Details: CHANGELOG.

What's New in v1.5

Feature Description
Tracing observability.tracing: spans per tool / resource / prompt call, W3C traceparent propagation, built-in OTLP/HTTP exporter or @opentelemetry/api
Prometheus /metrics conventional scrape path + latency histogram mcp_gateway_request_duration_seconds
Dashboard charts calls per server (calls / errors / p95) alongside rate, latency and error charts

Details: CHANGELOG.

What's New in v1.4

Feature Description
Shared state store state.store: redis — rate limits, auth lockouts and MCP sessions shared across replicas (built-in RESP client, failureMode), pluggable StateStore for embedders

Details: CHANGELOG.

What's New in v1.3

Feature Description
OAuth 2.1 (MCP authorization) auth.strategy: oauth2: RFC 9728 protected-resource metadata, JWT validation via (discovered) JWKS or RFC 7662 introspection, audience binding (RFC 8707), WWW-Authenticate challenges, requiredScopes
Resumable streams Last-Event-ID replay on GET /mcp from a per-session event buffer (mcp.eventBufferSize)

Details: CHANGELOG.

What's New in v1.2

Feature Description
Hashed & expiring keys auth.apiKeys entries can be sha256:<hex> digests (mcp-gateway gen-key, hash-key) and carry expiresAt / disabled
JWT hardening auth.jwt: issuer, audience, algorithms (HMAC / asymmetric never mixed), clockToleranceSeconds, requireExp, maxTokenAgeSeconds, PEM publicKey or cached jwksUrl (works with OAuth 2.0 / OIDC providers)
security block headers + dashboard CSP, hsts, trustProxy, ipAllowlist, allowedHosts, dnsRebindingProtection, maxBodyBytes, maxToolArgumentsBytes, authLockout, redactPatterns — all hot reloadable
Secure-defaults check startup warnings, validate --strict, GET /api/v1/security and a Security posture card in the dashboard
More of MCP on /mcp notifications/progress (SSE replies), logging/setLevel + forwarded notifications/message, completion/complete, resources/subscribe / unsubscribe + notifications/resources/updated

Details and upgrade notes: CHANGELOG.

What's New in v1.0

Feature Description
Stable API Semver from 1.0: /api/v1, /mcp, config keys, CLI and root exports are stable (see stability)
Docs API reference, configuration reference, deployment guide (Docker, Kubernetes, reverse proxy)
Container image Multi-arch ghcr.io/harrisoncn/mcp-gateway built on every release (Node 22)
Resources & prompts resources/list, resources/templates/list, resources/read, prompts/list, prompts/get aggregated on REST and /mcp, with list-changed notifications and scopes
Audit log Optional persistent request history in SQLite (node:sqlite), filterable / pageable GET /api/v1/requests, dashboard history
Clients & LLM schemas TypeScript client (clients/js), Kotlin client (clients/kotlin), GET /api/v1/tools?format=openai|openai-responses|anthropic
Per-key scopes API keys can carry servers / tools globs and their own rateLimit; JWTs carry mcp_servers / mcp_tools claims. Enforced on REST and /mcp, hot reloadable
/mcp endpoint The gateway is an MCP server (Streamable HTTP, 2025-06-18): sessions, aggregated + paginated tools/list, deterministic collision naming, routed tools/call, list_changed notifications, cancellation

What's New in v0.2.0

Feature Description
SSE Transport Transport class (src/transport/sse.ts); routable since the unreleased version
WebSocket Transport Transport class (src/transport/websocket.ts); routable since the unreleased version
Config Hot Reload Edit the server list in mcp-gateway.yml without restarting
Request Tracing X-Request-Id on every request & response
CORS Middleware Configurable cross-origin support
Web Dashboard Live monitoring UI at /dashboard
4 Bug Fixes Concurrency, id collision, handle leaks, timeouts

API stability

mcp-gateway follows Semantic Versioning since 1.0.0. Within a major version (3.x now) the REST API under /api/v1, the /mcp endpoint behaviour, configuration keys, CLI commands / flags, root library exports and Prometheus metric names only change in backward-compatible ways (new fields, endpoints and options may be added — ignore unknown fields). Deep imports, log format, the dashboard and the audit database schema are not covered. Details: docs/api-reference.md#stability-and-versioning.

Roadmap

3.0 completes the 1.x / 2.x plan (transports, /mcp endpoint, security, OAuth, tenants, policy, caching, plugins, edge runtimes, bridges, policy as code, declarative config). What comes next — sampling / elicitation passthrough, request replay, WASM plugins, smart routing, secrets management, federation, compliance, a developer portal and 4.0 — is in docs/ROADMAP.md.

Contributing

Contributions are welcome! See CONTRIBUTING.md.

git clone https://github.com/HarrisonCN/mcp-gateway.git
cd mcp-gateway
npm install
npm run typecheck && npm test
npm run dev -- start -c examples/basic/mcp-gateway.yml

License

MIT © 2026 HarrisonCN


Built for the agentic era · If this helps you, please ⭐ star the repo

来源:GitHub · github.com