Skip to content

BuildWithAbid/mcpgate

Repository files navigation

MCPGate

Open-source gateway for Model Context Protocol (MCP) servers.

MCPGate is a reverse proxy that sits between AI clients (Claude, Cursor, OpenAI Agents SDK) and your MCP servers. It adds authentication, rate limiting, access control, and observability — without modifying your MCP servers.

AI Client → MCPGate → MCP Server (GitHub)
                    → MCP Server (Postgres)
                    → MCP Server (Slack)

Why MCPGate?

MCP servers are powerful, but deploying them in production raises questions:

  • Who is calling my MCP server? MCPGate authenticates every request with API keys, JWT, or OIDC.
  • How do I prevent abuse? Token-bucket rate limiting with per-client and per-server controls.
  • Can I restrict which tools a client can use? Fine-grained access control policies with wildcard matching.
  • What's happening across my MCP servers? Prometheus metrics, structured audit logs, and request tracing.

Quick Start

Prerequisites

Install

git clone https://github.com/BuildWithAbid/mcpgate.git
cd mcpgate
npm install

Configure

Create config/mcpgate.yaml:

server:
  port: 3000

upstreams:
  github:
    url: "https://your-github-mcp-server.example.com/mcp"
    timeout: 30000

Run

# Development
npm run dev

# Production
npm run build
npm start

MCPGate is now proxying requests at http://localhost:3000/servers/github/mcp.

Configuration

MCPGate uses a single YAML config file. Every section except upstreams is optional — start simple and add features as you need them.

Upstream Servers

Each upstream gets its own route on the gateway:

upstreams:
  github:
    url: "https://github-mcp.example.com/mcp"
    timeout: 30000
    auth:
      type: bearer
      token_env: GITHUB_MCP_TOKEN    # Reads from environment variable

  postgres:
    url: "https://db-mcp.internal:8080/mcp"
    timeout: 15000
    auth:
      type: header
      header: X-DB-Key
      value_env: DB_MCP_KEY

Environment variables are interpolated with ${VAR_NAME} syntax.

Authentication

Authenticate clients connecting to the gateway. Choose one:

API Keys (simplest):

auth:
  type: api-key
  header: X-API-Key
  keys:
    - id: engineering
      key: "${ENG_API_KEY}"
    - id: contractors
      key: "${CONTRACTOR_API_KEY}"

JWT (OAuth 2.1):

auth:
  type: jwt
  jwks_uri: "https://auth.example.com/.well-known/jwks.json"
  issuer: "https://auth.example.com"
  audience: "mcpgate"
  algorithms: ["RS256"]
  client_id_claim: sub

OIDC (auto-discovery):

auth:
  type: oidc
  issuer: "https://accounts.google.com"
  audience: "your-client-id"
  client_id_claim: sub

Rate Limiting

Token-bucket rate limiting with configurable capacity and refill rate:

rate_limit:
  backend: memory         # memory (single instance) or redis (multi-instance)
  defaults:
    capacity: 100         # Max burst size
    refill_rate: 10       # Tokens per second

Rate-limited responses include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers.

Access Control

First-match-wins policy rules with wildcard support:

policy:
  default: allow
  rules:
    - clients: ["engineering"]
      servers: ["github", "postgres"]
      tools: ["*"]
      decision: allow

    - clients: ["contractors"]
      servers: ["github"]
      tools: ["search_*", "get_*"]      # Prefix wildcards
      decision: allow

    - clients: ["contractors"]
      servers: ["postgres"]
      decision: deny

Observability

observability:
  metrics:
    enabled: true
    path: /metrics            # Prometheus endpoint

  audit:
    enabled: true
    log_bodies: false         # Set true to log request/response bodies

Prometheus metrics exposed at /metrics:

Metric Type Labels
mcpgate_requests_total Counter server, method, status
mcpgate_request_duration_seconds Histogram server, method
mcpgate_active_sessions Gauge server
mcpgate_tool_calls_total Counter server, tool
mcpgate_rate_limit_rejections_total Counter client, server
mcpgate_policy_denials_total Counter client, server, tool

Full Example

See config/mcpgate.example.yaml for a complete annotated config.

CLI

mcpgate start [--config path]         # Start the gateway
mcpgate validate [--config path]      # Validate config without starting
mcpgate list-servers [--config path]  # List configured upstream servers

Docker

docker build -t mcpgate .
docker run -p 3000:3000 -v ./config:/app/config mcpgate

Or with Docker Compose:

docker compose up

How It Works

Request Flow

Client POST /servers/github/mcp
  │
  ├─ Auth middleware      → Validate API key / JWT / OIDC
  ├─ Rate limit middleware → Check token bucket
  ├─ Policy middleware     → Evaluate access control rules
  │
  └─ Proxy handler
       ├─ Map client session → upstream session
       ├─ Inject upstream credentials
       ├─ Forward request via fetch()
       └─ Stream SSE response back (zero buffering)

Session Management

MCPGate manages client-to-upstream session mapping automatically:

  1. Client sends initialize → gateway creates a session, forwards to upstream
  2. Gateway captures upstream's Mcp-Session-Id, maps it to a client-facing ID
  3. All subsequent requests have session IDs rewritten transparently
  4. Client sends DELETE → gateway cleans up and forwards to upstream

SSE Streaming

SSE responses from upstream servers are streamed chunk-by-chunk with zero buffering using ReadableStream passthrough. Long-running tool calls stream results in real time.

Architecture

src/
├── auth/           # API key, JWT, OIDC providers + middleware
├── cli/            # CLI commands (start, validate, list-servers)
├── config/         # Zod schema + YAML loader with env var interpolation
├── lib/            # Shared utilities (Result type, logger, JSON-RPC helpers)
├── observability/  # Prometheus metrics, audit logging, middleware
├── policy/         # Access control engine with wildcard matching
├── proxy/          # Core: session store, request parser, upstream client, SSE passthrough
├── ratelimit/      # Token bucket rate limiter + middleware
├── server/         # Hono app, routes, health endpoint, graceful shutdown
└── index.ts        # Entry point

Design Principles

  • Zero protocol participation — MCPGate proxies raw HTTP. It does not participate in MCP handshakes or modify JSON-RPC payloads.
  • Opt-in everything — Auth, rate limiting, policies, and observability are all optional. Start with just upstreams and add features as needed.
  • Functional core — Business logic uses Result<T, E> types instead of exceptions. Side effects are pushed to the edges.
  • Stream-first — SSE responses are never buffered. Backpressure propagates naturally through ReadableStream.

Testing

npm test              # Run all tests (113 tests)
npm run test:watch    # Watch mode
npm run typecheck     # Type checking

Roadmap

  • Redis-backed rate limiting for multi-instance deployments
  • OpenTelemetry distributed tracing
  • Hot config reload without dropping connections
  • Admin API for runtime session/config inspection
  • WebSocket transport support
  • Plugin system for custom middleware

Contributing

Contributions are welcome. Please open an issue first to discuss what you'd like to change.

License

MIT

About

Open-source MCP gateway — reverse proxy for Model Context Protocol servers with auth, rate limiting, access control, and observability

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages