MCP Gateway¶
Model Context Protocol gateway & proxy - unify REST, MCP, and A2A with federation, virtual servers, retries, security, and an optional admin UI.
ContextForge MCP Gateway is a feature-rich gateway, proxy and MCP Registry that federates MCP and REST services - unifying discovery, auth, rate-limiting, observability, virtual servers, multi-transport protocols, and an optional Admin UI into one clean endpoint for your AI clients. It runs as a fully compliant MCP server, deployable via PyPI or Docker, and scales to multi-cluster environments on Kubernetes with Redis-backed federation and caching.
π Overview & Goals¶
ContextForge MCP Gateway is a gateway, registry, and proxy that sits in front of any Model Context Protocol (MCP) server or REST API-exposing a unified endpoint for all your AI clients.
β οΈ Caution: The current release (0.6.0) is considered alpha / early beta. It is not production-ready and should only be used for local development, testing, or experimentation. Features, APIs, and behaviors are subject to change without notice. Do not deploy in production environments without thorough security review, validation and additional security mechanisms. Many of the features required for secure, large-scale, or multi-tenant production deployments are still on the project roadmap - which is itself evolving.
It currently supports:
- Federation across multiple MCP and REST services
- A2A (Agent-to-Agent) integration for external AI agents (OpenAI, Anthropic, custom)
- Virtualization of legacy APIs as MCP-compliant tools and servers
- Transport over HTTP, JSON-RPC, WebSocket, SSE (with configurable keepalive), stdio and streamable-HTTP
- An Admin UI for real-time management, configuration, and log monitoring
- Built-in auth, retries, and rate-limiting
- OpenTelemetry observability with Phoenix, Jaeger, Zipkin, and other OTLP backends
- Scalable deployments via Docker or PyPI, Redis-backed caching, and multi-cluster federation
For a list of upcoming features, check out the ContextForge MCP Gateway Roadmap
β οΈ Important: MCP Gateway is not a standalone product - it is an open source component with NO OFFICIAL SUPPORT from IBM or its affiliates that can be integrated into your own solution architecture. If you choose to use it, you are responsible for evaluating its fit, securing the deployment, and managing its lifecycle. See SECURITY.md for more details.
π Gateway Layer with Protocol Flexibility
- Sits in front of any MCP server or REST API
- Lets you choose your MCP protocol version (e.g.,
2025-03-26
) - Exposes a single, unified interface for diverse backends
π Federation of Peer Gateways (MCP Registry)
- Auto-discovers or configures peer gateways (via mDNS or manual)
- Performs health checks and merges remote registries transparently
- Supports Redis-backed syncing and fail-over
π§© Virtualization of REST/gRPC Services
- Wraps non-MCP services as virtual MCP servers
- Registers tools, prompts, and resources with minimal configuration
π REST-to-MCP Tool Adapter
-
Adapts REST APIs into tools with:
- Automatic JSON Schema extraction
- Support for headers, tokens, and custom auth
- Retry, timeout, and rate-limit policies
π§ Unified Registries
- Prompts: Jinja2 templates, multimodal support, rollback/versioning
- Resources: URI-based access, MIME detection, caching, SSE updates
- Tools: Native or adapted, with input validation and concurrency controls
π Admin UI, Observability & Dev Experience
- Admin UI built with HTMX + Alpine.js
- Real-time log viewer with filtering, search, and export capabilities
- Auth: Basic, JWT, or custom schemes
- Structured logs, health endpoints, metrics
- 400+ tests, Makefile targets, live reload, pre-commit hooks
π OpenTelemetry Observability
- Vendor-agnostic tracing with OpenTelemetry (OTLP) protocol support
- Multiple backend support: Phoenix (LLM-focused), Jaeger, Zipkin, Tempo, DataDog, New Relic
- Distributed tracing across federated gateways and services
- Automatic instrumentation of tools, prompts, resources, and gateway operations
- LLM-specific metrics: Token usage, costs, model performance
- Zero-overhead when disabled with graceful degradation
- Easy configuration via environment variables
Quick start with Phoenix (LLM observability):
# Start Phoenix
docker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latest
# Configure gateway
export OTEL_ENABLE_OBSERVABILITY=true
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# Run gateway - traces automatically sent to Phoenix
mcpgateway
See Observability Documentation for detailed setup with other backends.
Quick Start - PyPI¶
MCP Gateway is published on PyPI as mcp-contextforge-gateway
.
TLDR;: (single command using uv)
BASIC_AUTH_PASSWORD=pass \
MCPGATEWAY_UI_ENABLED=true \
MCPGATEWAY_ADMIN_API_ENABLED=true \
uvx --from mcp-contextforge-gateway mcpgateway --host 0.0.0.0 --port 4444
π Prerequisites
- Python β₯ 3.10 (3.11 recommended)
- curl + jq - only for the last smoke-test step
1 - Install & run (copy-paste friendly)¶
# 1οΈβ£ Isolated env + install from pypi
mkdir mcpgateway && cd mcpgateway
python3 -m venv .venv && source .venv/bin/activate
pip install --upgrade pip
pip install mcp-contextforge-gateway
# 2οΈβ£ Launch on all interfaces with custom creds & secret key
# Enable the Admin API endpoints (true/false) - disabled by default
export MCPGATEWAY_UI_ENABLED=true
export MCPGATEWAY_ADMIN_API_ENABLED=true
BASIC_AUTH_PASSWORD=pass JWT_SECRET_KEY=my-test-key \
mcpgateway --host 0.0.0.0 --port 4444 & # admin/pass
# 3οΈβ£ Generate a bearer token & smoke-test the API
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token \
--username admin --exp 10080 --secret my-test-key)
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://127.0.0.1:4444/version | jq
Windows (PowerShell) quick-start
# 1οΈβ£ Isolated env + install from PyPI
mkdir mcpgateway ; cd mcpgateway
python3 -m venv .venv ; .\.venv\Scripts\Activate.ps1
pip install --upgrade pip
pip install mcp-contextforge-gateway
# 2οΈβ£ Environment variables (session-only)
$Env:MCPGATEWAY_UI_ENABLED = "true"
$Env:MCPGATEWAY_ADMIN_API_ENABLED = "true"
$Env:BASIC_AUTH_PASSWORD = "changeme" # admin/changeme
$Env:JWT_SECRET_KEY = "my-test-key"
# 3οΈβ£ Launch the gateway
mcpgateway.exe --host 0.0.0.0 --port 4444
# Optional: background it
# Start-Process -FilePath "mcpgateway.exe" -ArgumentList "--host 0.0.0.0 --port 4444"
# 4οΈβ£ Bearer token and smoke-test
$Env:MCPGATEWAY_BEARER_TOKEN = python3 -m mcpgateway.utils.create_jwt_token `
--username admin --exp 10080 --secret my-test-key
curl -s -H "Authorization: Bearer $Env:MCPGATEWAY_BEARER_TOKEN" `
http://127.0.0.1:4444/version | jq
More configuration
Copy .env.example to .env
and tweak any of the settings (or use them as env variables).
π End-to-end demo (register a local MCP server)
# 1οΈβ£ Spin up the sample GO MCP time server using mcpgateway.translate & docker
python3 -m mcpgateway.translate \
--stdio "docker run --rm -i -p 8888:8080 ghcr.io/ibm/fast-time-server:latest -transport=stdio" \
--expose-sse \
--port 8003
# Or using the official mcp-server-git using uvx:
pip install uv # to install uvx, if not already installed
python3 -m mcpgateway.translate --stdio "uvx mcp-server-git" --expose-sse --port 9000
# Alternative: running the local binary
# cd mcp-servers/go/fast-time-server; make build
# python3 -m mcpgateway.translate --stdio "./dist/fast-time-server -transport=stdio" --expose-sse --port 8002
# NEW: Expose via multiple protocols simultaneously!
python3 -m mcpgateway.translate \
--stdio "uvx mcp-server-git" \
--expose-sse \
--expose-streamable-http \
--port 9000
# Now accessible via both /sse (SSE) and /mcp (streamable HTTP) endpoints
# 2οΈβ£ Register it with the gateway
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"fast_time","url":"http://localhost:9000/sse"}' \
http://localhost:4444/gateways
# 3οΈβ£ Verify tool catalog
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/tools | jq
# 4οΈβ£ Create a *virtual server* bundling those tools. Use the ID of tools from the tool catalog (Step #3) and pass them in the associatedTools list.
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"time_server","description":"Fast time tools","associatedTools":[<ID_OF_TOOLS>]}' \
http://localhost:4444/servers | jq
# Example curl
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN"
-H "Content-Type: application/json"
-d '{"name":"time_server","description":"Fast time tools","associatedTools":["6018ca46d32a4ac6b4c054c13a1726a2"]}' \
http://localhost:4444/servers | jq
# 5οΈβ£ List servers (should now include the UUID of the newly created virtual server)
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/servers | jq
# 6οΈβ£ Client SSE endpoint. Inspect it interactively with the MCP Inspector CLI (or use any MCP client)
npx -y @modelcontextprotocol/inspector
# Transport Type: SSE, URL: http://localhost:4444/servers/UUID_OF_SERVER_1/sse, Header Name: "Authorization", Bearer Token
π§ Using the stdio wrapper (mcpgateway-wrapper)
export MCP_AUTH=$MCPGATEWAY_BEARER_TOKEN
export MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp
python3 -m mcpgateway.wrapper # Ctrl-C to exit
You can also run it with uv
or inside Docker/Podman - see the Containers section above.
In MCP Inspector, define MCP_AUTH
and MCP_SERVER_URL
env variables, and select python3
as the Command, and -m mcpgateway.wrapper
as Arguments.
echo $PWD/.venv/bin/python3 # Using the Python3 full path ensures you have a working venv
export MCP_SERVER_URL='http://localhost:4444/servers/UUID_OF_SERVER_1/mcp'
export MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN}
npx -y @modelcontextprotocol/inspector
or
Pass the url and auth as arguments (no need to set environment variables)
npx -y @modelcontextprotocol/inspector
command as `python`
Arguments as `-m mcpgateway.wrapper --url "http://localhost:4444/servers/UUID_OF_SERVER_1/mcp" --auth "Bearer <your token>"`
When using a MCP Client such as Claude with stdio:
Quick Start - Containers¶
Use the official OCI image from GHCR with Docker or Podman.
π³ Docker¶
1 - Minimum viable run¶
docker run -d --name mcpgateway \
-p 4444:4444 \
-e MCPGATEWAY_UI_ENABLED=true \
-e MCPGATEWAY_ADMIN_API_ENABLED=true \
-e HOST=0.0.0.0 \
-e JWT_SECRET_KEY=my-test-key \
-e BASIC_AUTH_USER=admin \
-e BASIC_AUTH_PASSWORD=changeme \
-e AUTH_REQUIRED=true \
-e DATABASE_URL=sqlite:///./mcp.db \
ghcr.io/ibm/mcp-context-forge:0.6.0
# Tail logs (Ctrl+C to quit)
docker logs -f mcpgateway
# Generating an API key
docker run --rm -it ghcr.io/ibm/mcp-context-forge:0.6.0 \
python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 0 --secret my-test-key
Browse to http://localhost:4444/admin (user admin
/ pass changeme
).
2 - Persist the SQLite database¶
mkdir -p $(pwd)/data
touch $(pwd)/data/mcp.db
sudo chown -R :docker $(pwd)/data
chmod 777 $(pwd)/data
docker run -d --name mcpgateway \
--restart unless-stopped \
-p 4444:4444 \
-v $(pwd)/data:/data \
-e MCPGATEWAY_UI_ENABLED=true \
-e MCPGATEWAY_ADMIN_API_ENABLED=true \
-e DATABASE_URL=sqlite:////data/mcp.db \
-e HOST=0.0.0.0 \
-e JWT_SECRET_KEY=my-test-key \
-e BASIC_AUTH_USER=admin \
-e BASIC_AUTH_PASSWORD=changeme \
ghcr.io/ibm/mcp-context-forge:0.6.0
SQLite now lives on the host at ./data/mcp.db
.
3 - Local tool discovery (host network)¶
mkdir -p $(pwd)/data
touch $(pwd)/data/mcp.db
sudo chown -R :docker $(pwd)/data
chmod 777 $(pwd)/data
docker run -d --name mcpgateway \
--network=host \
-e MCPGATEWAY_UI_ENABLED=true \
-e MCPGATEWAY_ADMIN_API_ENABLED=true \
-e HOST=0.0.0.0 \
-e PORT=4444 \
-e DATABASE_URL=sqlite:////data/mcp.db \
-v $(pwd)/data:/data \
ghcr.io/ibm/mcp-context-forge:0.6.0
Using --network=host
allows Docker to access the local network, allowing you to add MCP servers running on your host. See Docker Host network driver documentation for more details.
π¦ Podman (rootless-friendly)¶
1 - Basic run¶
podman run -d --name mcpgateway \
-p 4444:4444 \
-e HOST=0.0.0.0 \
-e DATABASE_URL=sqlite:///./mcp.db \
ghcr.io/ibm/mcp-context-forge:0.6.0
2 - Persist SQLite¶
mkdir -p $(pwd)/data
touch $(pwd)/data/mcp.db
sudo chown -R :docker $(pwd)/data
chmod 777 $(pwd)/data
podman run -d --name mcpgateway \
--restart=on-failure \
-p 4444:4444 \
-v $(pwd)/data:/data \
-e DATABASE_URL=sqlite:////data/mcp.db \
ghcr.io/ibm/mcp-context-forge:0.6.0
3 - Host networking (rootless)¶
mkdir -p $(pwd)/data
touch $(pwd)/data/mcp.db
sudo chown -R :docker $(pwd)/data
chmod 777 $(pwd)/data
podman run -d --name mcpgateway \
--network=host \
-v $(pwd)/data:/data \
-e DATABASE_URL=sqlite:////data/mcp.db \
ghcr.io/ibm/mcp-context-forge:0.6.0
βοΈ Docker/Podman tips
- .env files - Put all the
-e FOO=
lines into a file and replace them with--env-file .env
. See the provided .env.example for reference. - Pinned tags - Use an explicit version (e.g.
v0.6.0
) instead oflatest
for reproducible builds. -
JWT tokens - Generate one in the running container:
-
Upgrades - Stop, remove, and rerun with the same
-v $(pwd)/data:/data
mount; your DB and config stay intact.
π Smoke-test the running container
π§ Running the MCP Gateway stdio wrapper
The mcpgateway.wrapper
lets you connect to the gateway over stdio while keeping JWT authentication. You should run this from the MCP Client. The example below is just for testing.
# Set environment variables
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 10080 --secret my-test-key)
export MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_URL='http://localhost:4444/servers/UUID_OF_SERVER_1/mcp'
export MCP_TOOL_CALL_TIMEOUT=120
export MCP_WRAPPER_LOG_LEVEL=DEBUG # or OFF to disable logging
docker run --rm -i \
-e MCP_AUTH=$MCPGATEWAY_BEARER_TOKEN \
-e MCP_SERVER_URL=http://host.docker.internal:4444/servers/UUID_OF_SERVER_1/mcp \
-e MCP_TOOL_CALL_TIMEOUT=120 \
-e MCP_WRAPPER_LOG_LEVEL=DEBUG \
ghcr.io/ibm/mcp-context-forge:0.6.0 \
python3 -m mcpgateway.wrapper
Testing mcpgateway.wrapper
by hand:¶
Because the wrapper speaks JSON-RPC over stdin/stdout, you can interact with it using nothing more than a terminal or pipes.
# Start the MCP Gateway Wrapper
export MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_URL=http://localhost:4444/servers/YOUR_SERVER_UUID
python3 -m mcpgateway.wrapper
Initialize the protocol
# Initialize the protocol
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"demo","version":"0.0.1"}}}
# Then after the reply:
{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}
# Get prompts
{"jsonrpc":"2.0","id":4,"method":"prompts/list"}
{"jsonrpc":"2.0","id":5,"method":"prompts/get","params":{"name":"greeting","arguments":{"user":"Bob"}}}
# Get resources
{"jsonrpc":"2.0","id":6,"method":"resources/list"}
{"jsonrpc":"2.0","id":7,"method":"resources/read","params":{"uri":"https://example.com/some.txt"}}
# Get / call tools
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_system_time","arguments":{"timezone":"Europe/Dublin"}}}
Expected responses from mcpgateway.wrapper
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"mcpgateway-wrapper","version":"0.6.0"}}}
# When there's no tools
{"jsonrpc":"2.0","id":2,"result":{"tools":[]}}
# After you add some tools and create a virtual server
{"jsonrpc":"2.0","id":2,"result":{"tools":[{"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":true},"description":"Convert time between different timezones","inputSchema":{"properties":{"source_timezone":{"description":"Source IANA timezone name","type":"string"},"target_timezone":{"description":"Target IANA timezone name","type":"string"},"time":{"description":"Time to convert in RFC3339 format or common formats like '2006-01-02 15:04:05'","type":"string"}},"required":["time","source_timezone","target_timezone"],"type":"object"},"name":"convert_time"},{"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":true},"description":"Get current system time in specified timezone","inputSchema":{"properties":{"timezone":{"description":"IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Defaults to UTC","type":"string"}},"type":"object"},"name":"get_system_time"}]}}
# Running the time tool:
{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"2025-07-09T00:09:45+01:00"}]}}
π§© Running from an MCP Client (mcpgateway.wrapper
)¶
The mcpgateway.wrapper
exposes everything your Gateway knows about over stdio, so any MCP client that can't (or shouldn't) open an authenticated SSE stream still gets full tool-calling power.
Remember to substitute your real Gateway URL (and server ID) for
http://localhost:4444/servers/UUID_OF_SERVER_1/mcp
. When inside Docker/Podman, that often becomeshttp://host.docker.internal:4444/servers/UUID_OF_SERVER_1/mcp
(macOS/Windows) or the gateway container's hostname (Linux).
π³ Docker / Podman
π¦ pipx (one-liner install & run)
# Install gateway package in its own isolated venv
pipx install --include-deps mcp-contextforge-gateway
# Run the stdio wrapper
MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN} \
MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \
python3 -m mcpgateway.wrapper
# Alternatively with uv
uv run --directory . -m mcpgateway.wrapper
Claude Desktop JSON (uses the host Python that pipx injected):
β‘ uv / uvx (light-speed venvs)
1 - Install uv
(uvx
is an alias it provides)¶
# (a) official one-liner
curl -Ls https://astral.sh/uv/install.sh | sh
# (b) or via pipx
pipx install uv
2 - Create an on-the-spot venv & run the wrapper¶
# Create venv in ~/.venv/mcpgateway (or current dir if you prefer)
uv venv ~/.venv/mcpgateway
source ~/.venv/mcpgateway/bin/activate
# Install the gateway package using uv
uv pip install mcp-contextforge-gateway
# Launch wrapper
MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN} \
MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \
uv run --directory . -m mcpgateway.wrapper # Use this just for testing, as the Client will run the uv command
Claude Desktop JSON (runs through uvx)¶
π Using with Claude Desktop (or any GUI MCP client)¶
- Edit Config β
File βΈ Settings βΈ Developer βΈ Edit Config
- Paste one of the JSON blocks above (Docker / pipx / uvx).
- Restart the app so the new stdio server is spawned.
- Open logs in the same menu to verify
mcpgateway-wrapper
started and listed your tools.
Need help? See:
- MCP Debugging Guide - https://modelcontextprotocol.io/docs/tools/debugging
π Quick Start: VS Code Dev Container¶
Spin up a fully-loaded dev environment (Python 3.11, Docker/Podman CLI, all project dependencies) in just two clicks.
π Prerequisites
- VS Code with the Dev Containers extension
- Docker or Podman installed and running locally
π§° Setup Instructions
1 - Clone & Open¶
VS Code will detect the .devcontainer
and prompt: "Reopen in Container" or manually run: Ctrl/Cmd β§ P β Dev Containers: Reopen in Container
2 - First-Time Build (Automatic)¶
The container build will:
- Install system packages & Python 3.11
- Run
make install-dev
to pull all dependencies - Execute tests to verify the toolchain
You'll land in /workspace
ready to develop.
π οΈ Daily Developer Workflow
Common tasks inside the container:
# Start dev server (hot reload)
make dev # http://localhost:4444
# Run tests & linters
make test
make lint
Optional:
make bash
- drop into an interactive shellmake clean
- clear build artefacts & caches- Port forwarding is automatic (customize via
.devcontainer/devcontainer.json
)
βοΈ GitHub Codespaces: 1-Click Cloud IDE
No local Docker? Use Codespaces:
- Go to the repo β Code βΈ Codespaces βΈ Create codespace on main
- Wait for the container image to build in the cloud
- Develop using the same workflow above
Quick Start (manual install)¶
Prerequisites¶
- Python β₯ 3.10
- GNU Make (optional, but all common workflows are available as Make targets)
- Optional: Docker / Podman for containerized runs
One-liner (dev)¶
What it does:
- Creates / activates a
.venv
in your home folder~/.venv/mcpgateway
- Installs the gateway and necessary dependencies
- Launches Gunicorn (Uvicorn workers) on http://localhost:4444
For development, you can use:
make install-dev # Install development dependencies, ex: linters and test harness
make lint # optional: run style checks (ruff, mypy, etc.)
Containerized (self-signed TLS)¶
Container Runtime Support¶
This project supports both Docker and Podman. The Makefile automatically detects which runtime is available and handles image naming differences.
Auto-detection¶
make container-build # Uses podman if available, otherwise docker
> You can use docker or podman, ex:
```bash
make podman # build production image
make podman-run-ssl # run at https://localhost:4444
# or listen on port 4444 on your host directly, adds --network=host to podman
make podman-run-ssl-host
Smoke-test the API¶
curl -k -sX GET \
-H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
https://localhost:4444/tools | jq
You should receive []
until you register a tool.
Installation¶
Via Make¶
UV (alternative)¶
uv venv && source .venv/bin/activate
uv pip install -e '.[dev]' # IMPORTANT: in zsh, quote to disable glob expansion!
pip (alternative)¶
Optional (PostgreSQL adapter)¶
You can configure the gateway with SQLite, PostgreSQL (or any other compatible database) in .env.
When using PostgreSQL, you need to install psycopg2
driver.
Quick Postgres container¶
docker run --name mcp-postgres \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=mysecretpassword \
-e POSTGRES_DB=mcp \
-p 5432:5432 -d postgres
A make compose-up
target is provided along with a docker-compose.yml file to make this process simpler.
Configuration (.env
or env vars)¶
β οΈ If any required
.env
variable is missing or invalid, the gateway will fail fast at startup with a validation error via Pydantic.
You can get started by copying the provided .env.example to .env
and making the necessary edits to fit your environment.
π§ Environment Configuration Variables
### Basic | Setting | Description | Default | Options | | --------------- | ---------------------------------------- | ---------------------- | ---------------------- | | `APP_NAME` | Gateway / OpenAPI title | `MCP Gateway` | string | | `HOST` | Bind address for the app | `127.0.0.1` | IPv4/IPv6 | | `PORT` | Port the server listens on | `4444` | 1-65535 | | `DATABASE_URL` | SQLAlchemy connection URL | `sqlite:///./mcp.db` | any SQLAlchemy dialect | | `APP_ROOT_PATH` | Subpath prefix for app (e.g. `/gateway`) | (empty) | string | | `TEMPLATES_DIR` | Path to Jinja2 templates | `mcpgateway/templates` | path | | `STATIC_DIR` | Path to static files | `mcpgateway/static` | path | > π‘ Use `APP_ROOT_PATH=/foo` if reverse-proxying under a subpath like `https://host.com/foo/`. ### Authentication | Setting | Description | Default | Options | | --------------------- | ---------------------------------------------------------------- | ------------- | ---------- | | `BASIC_AUTH_USER` | Username for Admin UI login and HTTP Basic authentication | `admin` | string | | `BASIC_AUTH_PASSWORD` | Password for Admin UI login and HTTP Basic authentication | `changeme` | string | | `AUTH_REQUIRED` | Require authentication for all API routes | `true` | bool | | `JWT_SECRET_KEY` | Secret key used to **sign JWT tokens** for API access | `my-test-key` | string | | `JWT_ALGORITHM` | Algorithm used to sign the JWTs (`HS256` is default, HMAC-based) | `HS256` | PyJWT algs | | `TOKEN_EXPIRY` | Expiry of generated JWTs in minutes | `10080` | int > 0 | | `AUTH_ENCRYPTION_SECRET` | Passphrase used to derive AES key for encrypting tool auth headers | `my-test-salt` | string | > π `BASIC_AUTH_USER`/`PASSWORD` are used for: > > * Logging into the web-based Admin UI > * Accessing APIs via Basic Auth (`curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN"`) > > π `JWT_SECRET_KEY` is used to: > > * Sign JSON Web Tokens (`Authorization: Bearerexport MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 0 --secret my-test-key)
echo $MCPGATEWAY_BEARER_TOKEN
# Default: stdout/stderr only (recommended for containers)
LOG_LEVEL=INFO
# No additional config needed - logs to stdout/stderr
# Optional: Enable file logging (no rotation)
LOG_TO_FILE=true
LOG_FOLDER=/var/log/mcpgateway
LOG_FILE=gateway.log
LOG_FILEMODE=a+
# Optional: Enable file logging with rotation
LOG_TO_FILE=true
LOG_ROTATION_ENABLED=true
LOG_MAX_SIZE_MB=10
LOG_BACKUP_COUNT=3
LOG_FOLDER=/var/log/mcpgateway
LOG_FILE=gateway.log
# Start Phoenix for LLM observability
docker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latest
# Configure gateway
export OTEL_ENABLE_OBSERVABILITY=true
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# Run gateway - traces automatically sent to Phoenix
mcpgateway
Running¶
Makefile¶
make serve # Run production Gunicorn server on
make serve-ssl # Run Gunicorn behind HTTPS on :4444 (uses ./certs)
Script helper¶
To run the development (uvicorn) server:
run.sh
is a wrapper arounduvicorn
that loads.env
, supports reload, and passes arguments to the server.
Key flags:
Flag | Purpose | Example |
---|---|---|
-e, --env FILE | load env-file | --env prod.env |
-H, --host | bind address | --host 127.0.0.1 |
-p, --port | listen port | --port 8080 |
-w, --workers | gunicorn workers | --workers 4 |
-r, --reload | auto-reload | --reload |
Manual (Uvicorn)¶
Authentication examples¶
# Generate a JWT token using JWT_SECRET_KEY and export it as MCPGATEWAY_BEARER_TOKEN
# Note that the module needs to be installed. If running locally use:
export MCPGATEWAY_BEARER_TOKEN=$(JWT_SECRET_KEY=my-test-key python3 -m mcpgateway.utils.create_jwt_token)
# Use the JWT token in an API call
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/tools
βοΈ AWS / Azure / OpenShift¶
Deployment details can be found in the GitHub Pages.
βοΈ IBM Cloud Code Engine Deployment¶
This project supports deployment to IBM Cloud Code Engine using the ibmcloud CLI and the IBM Container Registry.
βοΈ IBM Cloud Code Engine Deployment
### π§ Prerequisites - Podman **or** Docker installed locally - IBM Cloud CLI (use `make ibmcloud-cli-install` to install) - An [IBM Cloud API key](https://cloud.ibm.com/iam/apikeys) with access to Code Engine & Container Registry - Code Engine and Container Registry services **enabled** in your IBM Cloud account --- ### π¦ Environment Variables Create a **`.env`** file (or export the variables in your shell). The first block is **required**; the second provides **tunable defaults** you can override:# ββ Required βββββββββββββββββββββββββββββββββββββββββββββ
IBMCLOUD_REGION=us-south
IBMCLOUD_RESOURCE_GROUP=default
IBMCLOUD_PROJECT=my-codeengine-project
IBMCLOUD_CODE_ENGINE_APP=mcpgateway
IBMCLOUD_IMAGE_NAME=us.icr.io/myspace/mcpgateway:latest
IBMCLOUD_IMG_PROD=mcpgateway/mcpgateway
IBMCLOUD_API_KEY=your_api_key_here # Optional - omit to use interactive `ibmcloud login --sso`
# ββ Optional overrides (sensible defaults provided) ββββββ
IBMCLOUD_CPU=1 # vCPUs for the app
IBMCLOUD_MEMORY=4G # Memory allocation
IBMCLOUD_REGISTRY_SECRET=my-regcred # Name of the Container Registry secret
API Endpoints¶
You can test the API endpoints through curl, or Swagger UI, and check detailed documentation on ReDoc:
- Swagger UI β http://localhost:4444/docs
- ReDoc β http://localhost:4444/redoc
Generate an API Bearer token, and test the various API endpoints.
π Authentication & Health Checks
# Generate a bearer token using the configured secret key (use the same as your .env)
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token -u admin --secret my-test-key)
echo ${MCPGATEWAY_BEARER_TOKEN}
# Quickly confirm that authentication works and the gateway is healthy
curl -s -k -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" https://localhost:4444/health
# {"status":"healthy"}
# Quickly confirm the gateway version & DB connectivity
curl -s -k -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" https://localhost:4444/version | jq
π§± Protocol APIs (MCP) /protocol
# Initialize MCP session
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"protocol_version":"2025-03-26",
"capabilities":{},
"client_info":{"name":"MyClient","version":"1.0.0"}
}' \
http://localhost:4444/protocol/initialize
# Ping (JSON-RPC style)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}' \
http://localhost:4444/protocol/ping
# Completion for prompt/resource arguments (not implemented)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ref":{"type":"ref/prompt","name":"example_prompt"},
"argument":{"name":"topic","value":"py"}
}' \
http://localhost:4444/protocol/completion/complete
# Sampling (streaming) (not implemented)
curl -N -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messages":[{"role":"user","content":{"type":"text","text":"Hello"}}],
"maxTokens":16
}' \
http://localhost:4444/protocol/sampling/createMessage
π§ JSON-RPC Utility /rpc
Handles any method name: `list_tools`, `list_gateways`, `prompts/get`, or invokes a tool if method matches a registered tool name .π§ Tool Management /tools
# Register a new tool
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name":"clock_tool",
"url":"http://localhost:9000/rpc",
"description":"Returns current time",
"input_schema":{
"type":"object",
"properties":{"timezone":{"type":"string"}},
"required":[]
}
}' \
http://localhost:4444/tools
# List tools
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/tools
# Get tool by ID
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/tools/1
# Update tool
curl -X PUT -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "description":"Updated desc" }' \
http://localhost:4444/tools/1
# Toggle active status
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/tools/1/toggle?activate=false
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/tools/1/toggle?activate=true
# Delete tool
curl -X DELETE -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/tools/1
π€ A2A Agent Management /a2a
# Register a new A2A agent
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name":"hello_world_agent",
"endpoint_url":"http://localhost:9999/",
"agent_type":"jsonrpc",
"description":"External AI agent for hello world functionality",
"auth_type":"api_key",
"auth_value":"your-api-key",
"tags":["ai", "hello-world"]
}' \
http://localhost:4444/a2a
# List A2A agents
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/a2a
# Get agent by ID
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/a2a/agent-id
# Update agent
curl -X PUT -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "description":"Updated description" }' \
http://localhost:4444/a2a/agent-id
# Test agent (direct invocation)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"parameters": {
"method": "message/send",
"params": {
"message": {
"messageId": "test-123",
"role": "user",
"parts": [{"type": "text", "text": "Hello!"}]
}
}
},
"interaction_type": "test"
}' \
http://localhost:4444/a2a/agent-name/invoke
# Toggle agent status
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/a2a/agent-id/toggle?activate=false
# Delete agent
curl -X DELETE -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/a2a/agent-id
# Associate agent with virtual server (agents become available as MCP tools)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name":"AI Assistant Server",
"description":"Virtual server with AI agents",
"associated_a2a_agents":["agent-id"]
}' \
http://localhost:4444/servers
π Gateway Management /gateways
# Register an MCP server as a new gateway provider
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"peer_gateway","url":"http://peer:4444"}' \
http://localhost:4444/gateways
# List gateways
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/gateways
# Get gateway by ID
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/gateways/1
# Update gateway
curl -X PUT -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"description":"New description"}' \
http://localhost:4444/gateways/1
# Toggle active status
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/gateways/1/toggle?activate=false
# Delete gateway
curl -X DELETE -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/gateways/1
π Resource Management /resources
# Register resource
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"uri":"config://app/settings",
"name":"App Settings",
"content":"key=value"
}' \
http://localhost:4444/resources
# List resources
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/resources
# Read a resource
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/resources/config://app/settings
# Update resource
curl -X PUT -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"content":"new=value"}' \
http://localhost:4444/resources/config://app/settings
# Delete resource
curl -X DELETE -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/resources/config://app/settings
# Subscribe to updates (SSE)
curl -N -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/resources/subscribe/config://app/settings
π Prompt Management /prompts
# Create prompt template
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name":"greet",
"template":"Hello, {{ user }}!",
"argument_schema":{
"type":"object",
"properties":{"user":{"type":"string"}},
"required":["user"]
}
}' \
http://localhost:4444/prompts
# List prompts
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/prompts
# Get prompt (with args)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"user":"Alice"}' \
http://localhost:4444/prompts/greet
# Get prompt (no args)
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/prompts/greet
# Update prompt
curl -X PUT -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"template":"Hi, {{ user }}!"}' \
http://localhost:4444/prompts/greet
# Toggle active
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/prompts/5/toggle?activate=false
# Delete prompt
curl -X DELETE -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/prompts/greet
π² Root Management /roots
# List roots
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/roots
# Add root
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"uri":"/data","name":"Data Root"}' \
http://localhost:4444/roots
# Remove root
curl -X DELETE -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/roots/%2Fdata
# Subscribe to root changes (SSE)
curl -N -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/roots/changes
π₯οΈ Server Management /servers
# List servers
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/servers
# Get server
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/servers/UUID_OF_SERVER_1
# Create server
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"db","description":"Database","associatedTools": ["1","2","3"]}' \
http://localhost:4444/servers
# Update server
curl -X PUT -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"description":"Updated"}' \
http://localhost:4444/servers/UUID_OF_SERVER_1
# Toggle active
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/servers/UUID_OF_SERVER_1/toggle?activate=false
π Metrics /metrics
# Get aggregated metrics
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/metrics
# Reset metrics (all or per-entity)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/metrics/reset
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/metrics/reset?entity=tool&id=1
π‘ Events & Health
Full Swagger UI at `/docs`.π οΈ Sample Tool
Testing¶
Doctest Coverage¶
MCP Context Forge implements comprehensive doctest coverage to ensure all code examples in documentation are tested and verified:
make doctest # Run all doctests
make doctest-verbose # Run with detailed output
make doctest-coverage # Generate coverage report
make doctest-check # Check coverage percentage
Coverage Status: - β Transport Modules: 100% (base, stdio, SSE, WebSocket, streamable HTTP) - β Utility Functions: 100% (slug generation, JWT tokens, validation) - β Configuration: 100% (settings, environment variables) - π Service Classes: ~60% (in progress) - π Complex Classes: ~40% (in progress)
Benefits: - All documented examples are automatically tested - Documentation stays accurate and up-to-date - Developers can run examples directly from docstrings - Regression prevention through automated verification
For detailed information, see the Doctest Coverage Guide.
Project Structure¶
π Directory and file structure for mcpgateway
# ββββββββββ CI / Quality & Meta-files ββββββββββ
βββ .bumpversion.cfg # Automated semantic-version bumps
βββ .coveragerc # Coverage.py settings
βββ .darglint # Doc-string linter rules
βββ .dockerignore # Context exclusions for image builds
βββ .editorconfig # Consistent IDE / editor behaviour
βββ .env # Local runtime variables (git-ignored)
βββ .env.ce # IBM Code Engine runtime env (ignored)
βββ .env.ce.example # Sample env for IBM Code Engine
βββ .env.example # Generic sample env file
βββ .env.gcr # Google Cloud Run runtime env (ignored)
βββ .eslintrc.json # ESLint rules for JS / TS assets
βββ .flake8 # Flake-8 configuration
βββ .gitattributes # Git attributes (e.g. EOL normalisation)
βββ .github # GitHub settings, CI/CD workflows & templates
β βββ CODEOWNERS # Default reviewers
β βββ workflows/ # Bandit, Docker, CodeQL, Python Package, Container Deployment, etc.
βββ .gitignore # Git exclusion rules
βββ .hadolint.yaml # Hadolint rules for Dockerfiles
βββ .htmlhintrc # HTMLHint rules
βββ .markdownlint.json # Markdown-lint rules
βββ .pre-commit-config.yaml # Pre-commit hooks (ruff, black, mypy, ...)
βββ .pycodestyle # PEP-8 checker settings
βββ .pylintrc # Pylint configuration
βββ .pyspelling.yml # Spell-checker dictionary & filters
βββ .ruff.toml # Ruff linter / formatter settings
βββ .spellcheck-en.txt # Extra dictionary entries
βββ .stylelintrc.json # Stylelint rules for CSS
βββ .travis.yml # Legacy Travis CI config (reference)
βββ .whitesource # WhiteSource security-scanning config
βββ .yamllint # yamllint ruleset
# ββββββββββ Documentation & Guidance ββββββββββ
βββ CHANGELOG.md # Version-by-version change log
βββ CODE_OF_CONDUCT.md # Community behaviour guidelines
βββ CONTRIBUTING.md # How to file issues & send PRs
βββ DEVELOPING.md # Contributor workflows & style guide
βββ LICENSE # Apache License 2.0
βββ README.md # Project overview & quick-start
βββ SECURITY.md # Security policy & CVE disclosure process
βββ TESTING.md # Testing strategy, fixtures & guidelines
# ββββββββββ Containerisation & Runtime ββββββββββ
βββ Containerfile # OCI image build (Docker / Podman)
βββ Containerfile.lite # FROM scratch UBI-Micro production build
βββ docker-compose.yml # Local multi-service stack
βββ podman-compose-sonarqube.yaml # One-liner SonarQube stack
βββ run-gunicorn.sh # Opinionated Gunicorn startup script
βββ run.sh # Uvicorn shortcut with arg parsing
# ββββββββββ Build / Packaging / Tooling ββββββββββ
βββ MANIFEST.in # sdist inclusion rules
βββ Makefile # Dev & deployment targets
βββ package-lock.json # Deterministic npm lock-file
βββ package.json # Front-end / docs tooling deps
βββ pyproject.toml # Poetry / PDM config & lint rules
βββ sonar-code.properties # SonarQube analysis settings
βββ uv.lock # UV resolver lock-file
# ββββββββββ Kubernetes & Helm Assets ββββββββββ
βββ charts # Helm chart(s) for K8s / OpenShift
β βββ mcp-stack # Umbrella chart
β β βββ Chart.yaml # Chart metadata
β β βββ templates/... # Manifest templates
β β βββ values.yaml # Default values
β βββ README.md # Install / upgrade guide
βββ k8s # Raw (non-Helm) K8s manifests
β βββ *.yaml # Deployment, Service, PVC resources
# ββββββββββ Documentation Source ββββββββββ
βββ docs # MkDocs site source
β βββ base.yml # MkDocs "base" configuration snippet (do not modify)
β βββ mkdocs.yml # Site configuration (requires base.yml)
β βββ requirements.txt # Python dependencies for the MkDocs site
β βββ Makefile # Make targets for building/serving the docs
β βββ theme # Custom MkDocs theme assets
β βββ logo.png # Logo for the documentation theme
β βββ docs # Markdown documentation
β βββ architecture/ # ADRs for the project
β βββ articles/ # Long-form writeups
β βββ blog/ # Blog posts
β βββ deployment/ # Deployment guides (AWS, Azure, etc.)
β βββ development/ # Development workflows & CI docs
β βββ images/ # Diagrams & screenshots
β βββ index.md # Top-level docs landing page
β βββ manage/ # Management topics (backup, logging, tuning, upgrade)
β βββ overview/ # Feature overviews & UI documentation
β βββ security/ # Security guidance & policies
β βββ testing/ # Testing strategy & fixtures
β βββ using/ # User-facing usage guides (agents, clients, etc.)
β βββ media/ # Social media, press coverage, videos & testimonials
β β βββ press/ # Press articles and blog posts
β β βββ social/ # Tweets, LinkedIn posts, YouTube embeds
β β βββ testimonials/ # Customer quotes & community feedback
β β βββ kit/ # Media kit & logos for bloggers & press
βββ dictionary.dic # Custom dictionary for spell-checker (make spellcheck)
# ββββββββββ Application & Libraries ββββββββββ
βββ agent_runtimes # Configurable agentic frameworks converted to MCP Servers
βββ mcpgateway # β main application package
β βββ __init__.py # Package metadata & version constant
β βββ admin.py # FastAPI routers for Admin UI
β βββ cache
β β βββ __init__.py
β β βββ resource_cache.py # LRU+TTL cache implementation
β β βββ session_registry.py # Session β cache mapping
β βββ config.py # Pydantic settings loader
β βββ db.py # SQLAlchemy models & engine setup
β βββ federation
β β βββ __init__.py
β β βββ discovery.py # Peer-gateway discovery
β β βββ forward.py # RPC forwarding
β βββ handlers
β β βββ __init__.py
β β βββ sampling.py # Streaming sampling handler
β βββ main.py # FastAPI app factory & startup events
β βββ mcp.db # SQLite fixture for tests
β βββ py.typed # PEP 561 marker (ships type hints)
β βββ schemas.py # Shared Pydantic DTOs
β βββ services
β β βββ __init__.py
β β βββ completion_service.py # Prompt / argument completion
β β βββ gateway_service.py # Peer-gateway registry
β β βββ logging_service.py # Central logging helpers
β β βββ prompt_service.py # Prompt CRUD & rendering
β β βββ resource_service.py # Resource registration & retrieval
β β βββ root_service.py # File-system root registry
β β βββ server_service.py # Server registry & monitoring
β β βββ tool_service.py # Tool registry & invocation
β βββ static
β β βββ admin.css # Styles for Admin UI
β β βββ admin.js # Behaviour for Admin UI
β βββ templates
β β βββ admin.html # HTMX/Alpine Admin UI template
β βββ transports
β β βββ __init__.py
β β βββ base.py # Abstract transport interface
β β βββ sse_transport.py # Server-Sent Events transport
β β βββ stdio_transport.py # stdio transport for embedding
β β βββ websocket_transport.py # WS transport with ping/pong
β βββ models.py # Core enums / type aliases
β βββ utils
β β βββ create_jwt_token.py # CLI & library for JWT generation
β β βββ services_auth.py # Service-to-service auth dependency
β β βββ verify_credentials.py # Basic / JWT auth helpers
β βββ validation
β β βββ __init__.py
β β βββ jsonrpc.py # JSON-RPC 2.0 validation
β βββ version.py # Library version helper
βββ mcpgateway-wrapper # Stdio client wrapper (PyPI)
β βββ pyproject.toml
β βββ README.md
β βββ src/mcpgateway_wrapper/
β βββ __init__.py
β βββ server.py # Wrapper entry-point
βββ mcp-servers # Sample downstream MCP servers
βββ mcp.db # Default SQLite DB (auto-created)
βββ mcpgrid # Experimental grid client / PoC
βββ os_deps.sh # Installs system-level deps for CI
# ββββββββββ Tests & QA Assets ββββββββββ
βββ test_readme.py # Guard: README stays in sync
βββ tests
β βββ conftest.py # Shared fixtures
β βββ e2e/... # End-to-end scenarios
β βββ hey/... # Load-test logs & helper script
β βββ integration/... # API-level integration tests
β βββ unit/... # Pure unit tests for business logic
API Documentation¶
- Swagger UI β http://localhost:4444/docs
- ReDoc β http://localhost:4444/redoc
- Admin Panel β http://localhost:4444/admin
Makefile targets¶
This project offer the following Makefile targets. Type make
in the project root to show all targets.
π§ Available Makefile targets
π MCP CONTEXT FORGE (An enterprise-ready Model Context Protocol Gateway)
π§ SYSTEM-LEVEL DEPENDENCIES (DEV BUILD ONLY)
os-deps - Install Graphviz, Pandoc, Trivy, SCC used for dev docs generation and security scan
π± VIRTUAL ENVIRONMENT & INSTALLATION
venv - Create a fresh virtual environment with uv & friends
activate - Activate the virtual environment in the current shell
install - Install project into the venv
install-dev - Install project (incl. dev deps) into the venv
install-db - Install project (incl. postgres and redis) into venv
update - Update all installed deps inside the venv
check-env - Verify all required env vars in .env are present
βΆοΈ SERVE & TESTING
serve - Run production Gunicorn server on :4444
certs - Generate self-signed TLS cert & key in ./certs (won't overwrite)
serve-ssl - Run Gunicorn behind HTTPS on :4444 (uses ./certs)
dev - Run fast-reload dev server (uvicorn)
run - Execute helper script ./run.sh
test - Run unit tests with pytest
test-curl - Smoke-test API endpoints with curl script
pytest-examples - Run README / examples through pytest-examples
clean - Remove caches, build artefacts, virtualenv, docs, certs, coverage, SBOM, etc.
π COVERAGE & METRICS
coverage - Run tests with coverage, emit md/HTML/XML + badge
pip-licenses - Produce dependency license inventory (markdown)
scc - Quick LoC/complexity snapshot with scc
scc-report - Generate HTML LoC & per-file metrics with scc
π DOCUMENTATION & SBOM
docs - Build docs (graphviz + handsdown + images + SBOM)
images - Generate architecture & dependency diagrams
π LINTING & STATIC ANALYSIS
lint - Run the full linting suite (see targets below)
black - Reformat code with black
autoflake - Remove unused imports / variables with autoflake
isort - Organise & sort imports with isort
flake8 - PEP-8 style & logical errors
pylint - Pylint static analysis
markdownlint - Lint Markdown files with markdownlint (requires markdownlint-cli)
mypy - Static type-checking with mypy
bandit - Security scan with bandit
pydocstyle - Docstring style checker
pycodestyle - Simple PEP-8 checker
pre-commit - Run all configured pre-commit hooks
ruff - Ruff linter + formatter
ty - Ty type checker from astral
pyright - Static type-checking with Pyright
radon - Code complexity & maintainability metrics
pyroma - Validate packaging metadata
importchecker - Detect orphaned imports
spellcheck - Spell-check the codebase
fawltydeps - Detect undeclared / unused deps
wily - Maintainability report
pyre - Static analysis with Facebook Pyre
depend - List dependencies in βrequirements format
snakeviz - Profile & visualise with snakeviz
pstats - Generate PNG call-graph from cProfile stats
spellcheck-sort - Sort local spellcheck dictionary
tox - Run tox across multi-Python versions
sbom - Produce a CycloneDX SBOM and vulnerability scan
pytype - Flow-sensitive type checker
check-manifest - Verify sdist/wheel completeness
yamllint - Lint YAML files (uses .yamllint)
jsonlint - Validate every *.json file with jq (--exit-status)
tomllint - Validate *.toml files with tomlcheck
πΈοΈ WEBPAGE LINTERS & STATIC ANALYSIS (HTML/CSS/JS lint + security scans + formatting)
install-web-linters - Install HTMLHint, Stylelint, ESLint, Retire.js & Prettier via npm
lint-web - Run HTMLHint, Stylelint, ESLint, Retire.js and npm audit
format-web - Format HTML, CSS & JS files with Prettier
osv-install - Install/upgrade osv-scanner (Go)
osv-scan-source - Scan source & lockfiles for CVEs
osv-scan-image - Scan the built container image for CVEs
osv-scan - Run all osv-scanner checks (source, image, licence)
π‘ SONARQUBE ANALYSIS
sonar-deps-podman - Install podman-compose + supporting tools
sonar-deps-docker - Install docker-compose + supporting tools
sonar-up-podman - Launch SonarQube with podman-compose
sonar-up-docker - Launch SonarQube with docker-compose
sonar-submit-docker - Run containerized Sonar Scanner CLI with Docker
sonar-submit-podman - Run containerized Sonar Scanner CLI with Podman
pysonar-scanner - Run scan with Python wrapper (pysonar-scanner)
sonar-info - How to create a token & which env vars to export
π‘οΈ SECURITY & PACKAGE SCANNING
trivy - Scan container image for CVEs (HIGH/CRIT). Needs podman socket enabled
grype-scan - Scan container for security audit and vulnerability scanning
dockle - Lint the built container image via tarball (no daemon/socket needed)
hadolint - Lint Containerfile/Dockerfile(s) with hadolint
pip-audit - Audit Python dependencies for published CVEs
π¦ DEPENDENCY MANAGEMENT
deps-update - Run update-deps.py to update all dependencies in pyproject.toml and docs/requirements.txt
containerfile-update - Update base image in Containerfile to latest tag
π¦ PACKAGING & PUBLISHING
dist - Clean-build wheel *and* sdist into ./dist
wheel - Build wheel only
sdist - Build source distribution only
verify - Build + twine + check-manifest + pyroma (no upload)
publish - Verify, then upload to PyPI (needs TWINE_* creds)
π¦ PODMAN CONTAINER BUILD & RUN
podman-dev - Build development container image
podman - Build container image
podman-prod - Build production container image (using ubi-micro β scratch). Not supported on macOS.
podman-run - Run the container on HTTP (port 4444)
podman-run-shell - Run the container on HTTP (port 4444) and start a shell
podman-run-ssl - Run the container on HTTPS (port 4444, self-signed)
podman-run-ssl-host - Run the container on HTTPS with --network=host (port 4444, self-signed)
podman-stop - Stop & remove the container
podman-test - Quick curl smoke-test against the container
podman-logs - Follow container logs (βC to quit)
podman-stats - Show container resource stats (if supported)
podman-top - Show live top-level process info in container
podman-shell - Open an interactive shell inside the Podman container
π DOCKER BUILD & RUN
docker-dev - Build development Docker image
docker - Build production Docker image
docker-prod - Build production container image (using ubi-micro β scratch). Not supported on macOS.
docker-run - Run the container on HTTP (port 4444)
docker-run-ssl - Run the container on HTTPS (port 4444, self-signed)
docker-stop - Stop & remove the container
docker-test - Quick curl smoke-test against the container
docker-logs - Follow container logs (βC to quit)
docker-stats - Show container resource usage stats (non-streaming)
docker-top - Show top-level process info in Docker container
docker-shell - Open an interactive shell inside the Docker container
π οΈ COMPOSE STACK - Build / start / stop the multi-service stack
compose-up - Bring the whole stack up (detached)
compose-restart - Recreate changed containers, pulling / building as needed
compose-build - Build (or rebuild) images defined in the compose file
compose-pull - Pull the latest images only
compose-logs - Tail logs from all services (Ctrl-C to exit)
compose-ps - Show container status table
compose-shell - Open an interactive shell in the "gateway" container
compose-stop - Gracefully stop the stack (keep containers)
compose-down - Stop & remove containers (keep named volumes)
compose-rm - Remove *stopped* containers
compose-clean - β¨ Down **and** delete named volumes (data-loss β )
βοΈ IBM CLOUD CODE ENGINE
ibmcloud-check-env - Verify all required IBM Cloud env vars are set
ibmcloud-cli-install - Auto-install IBM Cloud CLI + required plugins (OS auto-detected)
ibmcloud-login - Login to IBM Cloud CLI using IBMCLOUD_API_KEY (--sso)
ibmcloud-ce-login - Set Code Engine target project and region
ibmcloud-list-containers - List deployed Code Engine apps
ibmcloud-tag - Tag container image for IBM Container Registry
ibmcloud-push - Push image to IBM Container Registry
ibmcloud-deploy - Deploy (or update) container image in Code Engine
ibmcloud-ce-logs - Stream logs for the deployed application
ibmcloud-ce-status - Get deployment status
ibmcloud-ce-rm - Delete the Code Engine application
π§ͺ MINIKUBE LOCAL CLUSTER
minikube-install - Install Minikube (macOS, Linux, or Windows via choco)
helm-install - Install Helm CLI (macOS, Linux, or Windows)
minikube-start - Start local Minikube cluster with Ingress + DNS + metrics-server
minikube-stop - Stop the Minikube cluster
minikube-delete - Delete the Minikube cluster
minikube-image-load - Build and load ghcr.io/ibm/mcp-context-forge:latest into Minikube
minikube-k8s-apply - Apply Kubernetes manifests from deployment/k8s/
minikube-status - Show status of Minikube and ingress pods
π οΈ HELM CHART TASKS
helm-lint - Lint the Helm chart (static analysis)
helm-package - Package the chart into dist/ as mcp-stack-<ver>.tgz
helm-deploy - Upgrade/Install chart into Minikube (profile mcpgw)
helm-delete - Uninstall the chart release from Minikube
π LOCAL PYPI SERVER
local-pypi-install - Install pypiserver for local testing
local-pypi-start - Start local PyPI server on :8084 (no auth)
local-pypi-start-auth - Start local PyPI server with basic auth (admin/admin)
local-pypi-stop - Stop local PyPI server
local-pypi-upload - Upload existing package to local PyPI (no auth)
local-pypi-upload-auth - Upload existing package to local PyPI (with auth)
local-pypi-test - Install package from local PyPI
local-pypi-clean - Full cycle: build β upload β install locally
π LOCAL DEVPI SERVER
devpi-install - Install devpi server and client
devpi-init - Initialize devpi server (first time only)
devpi-start - Start devpi server
devpi-stop - Stop devpi server
devpi-setup-user - Create user and dev index
devpi-upload - Upload existing package to devpi
devpi-test - Install package from devpi
devpi-clean - Full cycle: build β upload β install locally
devpi-status - Show devpi server status
devpi-web - Open devpi web interface
π Troubleshooting¶
Port publishing on WSL2 (rootless Podman & Docker Desktop)
### Diagnose the listener *Seeing `:::4444 LISTEN rootlessport` is normal* - the IPv6 wildcard socket (`::`) also accepts IPv4 traffic **when** `net.ipv6.bindv6only = 0` (default on Linux). ### Why localhost fails on Windows WSL 2's NAT layer rewrites only the *IPv6* side of the dual-stack listener. From Windows, `http://127.0.0.1:4444` (or Docker Desktop's "localhost") therefore times-out. #### Fix (Podman rootless) `ss` should now show `0.0.0.0:4444` instead of `:::4444`, and the service becomes reachable from Windows *and* the LAN. #### Fix (Docker Desktop > 4.19) Docker Desktop adds a "WSL integration" switch per-distro. Turn it **on** for your distro, restart Docker Desktop, then restart the container:Gateway starts but immediately exits ("Failed to read DATABASE_URL")
Copy `.env.example` to `.env` first: Then edit `DATABASE_URL`, `JWT_SECRET_KEY`, `BASIC_AUTH_PASSWORD`, etc. Missing or empty required vars cause a fast-fail at startup.Contributing¶
- Fork the repo, create a feature branch.
- Run
make lint
and fix any issues. - Keep
make test
green and 100% coverage. - Open a PR - describe your changes clearly.
See CONTRIBUTING.md for more details.¶
Changelog¶
A complete changelog can be found here: CHANGELOG.md
License¶
Licensed under the Apache License 2.0 - see LICENSE
Core Authors and Maintainers¶
- Mihai Criveti - Distinguished Engineer, Agentic AI
Special thanks to our contributors for helping us improve ContextForge MCP Gateway: