Skip to main content
Glama
freemode1614

mcp-gateway

by freemode1614

mcp-gateway-poc

A local MCP Gateway that aggregates multiple MCP backend servers behind a single HTTP/SSE endpoint. One client connection, all tools and resources from every backend visible under a single namespace.

Why

Most MCP clients only connect to one endpoint. With many MCP servers (GitHub, Jira, custom internal tools), you end up with N separate client configurations and no unified observability. mcp-gateway solves this by acting as a local proxy:

Client ──HTTP/SSE──▶ mcp-gateway ──┬─ stdio subprocess  ─▶ Backend A
                                   └─ HTTP/SSE          ─▶ Backend B

Each backend's tools/resources are exposed with a <backend>. prefix so callers always know which backend is answering.

Related MCP server: MCP Proxy Server

Features (PoC scope)

  • HTTP/SSE frontend on 127.0.0.1 (default port 8765)

  • Backend transports: stdio subprocess and HTTP/SSE

  • Aggregated tool and resource catalogs

  • <backend>.<tool> and <backend>://<resource> namespace

  • YAML configuration with ${env:VAR} interpolation

  • Hot reload on config edit (add/remove/restart diffed, others unaffected)

  • Structured JSON logging with request_id propagation

  • GET /health for per-backend status

  • Structured lifecycle logs (startup, backend_connected, reload_applied, …)

Out of scope for the PoC: authentication, prompts/sampling, persistent state, multi-tenant quotas.

Install

Requires Python 3.13 and uv.

uv venv
uv pip install -e ".[dev]"

Quick start (two commands)

Terminal 1 — start the gateway with the example config:

export PYTHON="$(pwd)/.venv/bin/python"   # so the stdio mock can import `mcp`
mcp-gateway --config examples/mcp-gateway.yaml

Terminal 2 — query the gateway:

curl http://127.0.0.1:8765/health

# JSON-RPC initialize
curl -X POST http://127.0.0.1:8765/messages \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'

# List tools (prefixed with backend name)
curl -X POST http://127.0.0.1:8765/messages \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

# Call a tool (note the "mock." prefix)
curl -X POST http://127.0.0.1:8765/messages \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"mock.add","arguments":{"a":2,"b":3}}}'

Open the SSE stream:

curl -N http://127.0.0.1:8765/sse

The first event is event: endpoint with the absolute URL to POST to.

Configuration

gateway:
  host: 127.0.0.1
  port: 8765
  log_level: info
  reload:
    enabled: true
    debounce_ms: 500

backends:
  - name: github               # unique, used as tool prefix
    transport: stdio
    command: uvx
    args: [mcp-server-github]
    env:
      GITHUB_TOKEN: "${env:GITHUB_TOKEN}"

  - name: jira
    transport: sse
    url: http://127.0.0.1:9001/sse
    headers:
      Authorization: "Bearer ${env:JIRA_TOKEN}"

Backend name must match ^[a-z0-9_-]+$. Edit the file while the gateway is running — within the debounce window, the gateway will add / remove / restart the affected backends and emit a reload_applied log line.

CLI flags

  • --config <path>: explicit config file (else: MCP_GATEWAY_CONFIG env var, ./mcp-gateway.yaml, ~/.config/mcp-gateway/config.yaml)

  • --host <host>: override gateway.host

  • --port <port>: override gateway.port

  • --log-level <level>: override gateway.log_level

Tests

uv run pytest            # 78 tests, all layers
uv run pytest --cov=src/mcp_gateway   # with coverage report

Coverage targets for the PoC: ≥ 80% overall, ≥ 90% on src/mcp_gateway/core/.

Architecture

┌──────────────────────────────────────────────────────────────────────┐
│                         MCP Gateway (PoC)                            │
│   ┌─────────────────────────────────────────────────────────────┐    │
│   │ Frontend (FastAPI)                                           │    │
│   │   GET  /sse       → SSE stream (MCP transport)              │    │
│   │   POST /messages  → JSON-RPC dispatcher                      │    │
│   │   GET  /health     → per-backend status                      │    │
│   └─────────────────────────────────────────────────────────────┘    │
│   ┌─────────────────────────────────────────────────────────────┐    │
│   │ Core                                                          │    │
│   │   Registry     – in-memory catalog, tools/resources          │    │
│   │   Router       – dispatch tools/call, resources/read         │    │
│   │   Manager      – start/stop backends, hot-reload diff         │    │
│   └─────────────────────────────────────────────────────────────┘    │
│   ┌─────────────────────────────────────────────────────────────┐    │
│   │ Backend adapters                                             │    │
│   │   StdioBackend       → mcp.client.stdio.stdio_client         │    │
│   │   HttpSseBackend     → mcp.client.sse.sse_client             │    │
│   └─────────────────────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────────────────────┘

Boundary rules:

  • Frontend only speaks MCP wire protocol — knows nothing about backends.

  • Registry / Router are transport-agnostic — they only see prefixed names.

  • Adapters are the only layer that imports the mcp SDK — upgrades are local.

Spec / plan

This PoC was built against the OpenSpec change at openspec/changes/mcp-gateway-poc/. To inspect or re-validate:

openspec status --change mcp-gateway-poc
openspec validate mcp-gateway-poc
openspec show mcp-gateway-poc

When the implementation is accepted, archive it with /opsx-archive to merge the specs into openspec/specs/.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    C
    maintenance
    A flexible proxy server that aggregates multiple backend MCP servers into a single interface using STDIO or SSE transports. It supports dynamic server management via an HTTP API and utilizes namespacing to prevent tool conflicts across connected services.
    3
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Aggregates multiple backend MCP servers into a single unified interface with optional web management UI for tool control and configuration.
    60 npm
    193
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Aggregates multiple MCP servers into a single HTTP endpoint with tool namespacing, dashboard, and REST API for management.
    13 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Aggregates multiple upstream MCP servers (stdio, streamable-http, sse, websocket) behind a single streamable-HTTP endpoint with declarative configuration, namespacing, filtering, and auto-reload.
    MIT