A flexible feature-rich FastAPI-based gateway for the Model Context Protocol (MCP) that unifies and federates tools, resources, prompts, servers and peer gateways, wraps any REST API as MCP-compliant tools or virtual servers, and exposes everything over HTTP/JSON-RPC, WebSocket, Server-Sent Events (SSE) and stdio transportsβall manageable via a rich, interactive Admin UI and packaged as a container with support for any SQLAlchemy supported database.
MCP Gateway builds on the MCP spec by sitting in front of MCP Server or REST API to:
- Act as a true gateway, centralizing tool, resource and prompt registries while preserving the official MCP 2025-03-26 protocol
- Federate multiple MCP servers into one unified endpointβauto-discover peers (mDNS or explicit), health-check them, and merge their capabilities
- Virtualize non-MCP services as "virtual servers" so you can register any REST API or function endpoint and expose it under MCP semantics
- Adapt arbitrary REST/HTTP APIs into MCP tools with JSON-Schema input validation, retry/rate-limit policies and transparent JSON-RPC invocation
- Simplify deployments with a full admin UI, rich transports, pre-built DX pipelines and production-grade observability
-
Full MCP 2025-03-26: initialize, ping, notify, completion, sampling (SSE), plus JSON-RPC fallback
-
Gateway Layer: sits alongside or in front of MCP servers, enforcing MCP rules and consolidating multiple backends
-
Multi-Transport: HTTP/JSON-RPC, WebSocket (ping/pong), SSE (one-way + backchannel), stdio
-
Federation:
- Auto-discover or configure peer gateways
- Periodic health checks with fail-over
- Transparent merging of remote registries into one catalog
-
Virtual Servers: wrap any non-MCP endpoint (REST, gRPC, function) as a managed MCP server with minimal config
-
REST-to-MCP Adapter: register any REST API as an MCP toolβautomatic schema extraction, auth headers, retry/rate limits
-
Resources: templated URIs, LRU+TTL caching, MIME detection, real-time SSE subscriptions
-
Prompts: Jinja2 templates, JSON-Schema enforcement, multimodal blocks, versioning & rollback
-
Tools: MCP-native or REST-based; input validation, retry logic, rate-limit/concurrency controls
- Admin UI (HTMX + Alpine.js + Tailwind): full CRUD for servers, tools, resources, prompts, gateways, roots & metrics
- Authentication & Authorization: Basic, JWT Bearer, custom header schemes, per-endpoint DI
- Persistence & Migrations: async SQLAlchemy ORM (SQLite, Postgres, MySQL, etc.), Alembic auto-migrations
- Event System: uniform event envelopes on WS/SSE fan-out, server-side filters, backchannel hooks
- Observability & Health: structured JSON logs,
/health
latency metrics decorator on every handler - Developer Experience: Makefile targets, pre-commit (
ruff
,black
,mypy
,isort
), live-reload, 400+ tests, CI badges
MCP Gateway is published on PyPi as mcp-contextforge-gateway
. You can install and start a server with:
# Create a virtual environment and activate it
mkdir mcpgateway && cd mcpgateway # directory to store Python venv and mcp.db
python3 -m venv .venv
. ./.venv/bin/activate
# Install mcp-contextforge-gateway
pip install mcp-contextforge-gateway
# Run mcpgateway with default options (binds to 127.0.0.1:4444) with admin:changeme
mcpgateway # login to http://127.0.0.1:4444
# Optional: run in background with configured password/key and listen to all IPs
BASIC_AUTH_PASSWORD=password JWT_SECRET_KEY=my-test-key mcpgateway --host 0.0.0.0 --port 4444 & bg
# List all options
mcpgateway --help
# Generate your JWT token from the key
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 10080 --secret my-test-key)
# Run a local MCP Server (github) listening on SSE http://localhost:8000/sse
pip install uvenv
npx -y supergateway --stdio "uvenv run mcp-server-git"
#--------------------------------------------
# Register the MCP Server with the gateway and test it
# The curl steps can also from the admin ui: http://localhost:4444/admin
# For more info on the API see /docs and /redoc *after* login to /admin
#---------------------------------------------
# Test the API (assume you have curl and jq installed)
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/version | jq
# Register the MCP server as a new gateway provider
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"github_mcp_server","url":"http://localhost:8000/sse"}' \
http://localhost:4444/gateways
# List gateways - you should see [{"id":1,"name":"github_mcp_server","url":"http://localhost:8000/sse" ...
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/gateways | jq
# Get gateway by ID
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/gateways/1
# List the global tools
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/tools | jq
# Create a virtual server with tool 1,2,3 form global tools catalog
# You can configure virtual servers with multiple tools/resources/prompts from registered MCP server (gateways)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"devtools_mcp_server","description":"My developer tools","associatedTools": ["1","2","3"]}' \
http://localhost:4444/servers
# List servers
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/servers
# Get an individual server
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/servers/1
# You can now use http://localhost:4444/servers/1 as an SSE server with the configured JWT token in any MCP client
# To stop the running process, you can either:
fg # Return the process to foreground, you can not Ctrl + C, or:
pkill mcpgateway
See .env.example for full list of ENV variables you can use to override the configuration.
If you just want to run the gateway using the official OCI container image from GitHub Container Registry:
docker run -d --name mcpgateway \
-p 4444:4444 \
-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:latest
docker logs mcpgateway
You can now access the UI at http://localhost:4444/admin
π‘ You can also use
--env-file .env
if you have a config file already. See the provided .env.example π‘ To access local tools, consider using--network=host
π‘ Consider using a stable / release version of the image, ex:ghcr.io/ibm/mcp-context-forge:v0.1.0
-v $(pwd)/data:/app
export MCPGATEWAY_BEARER_TOKEN=$(docker exec mcpgateway python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 100800 --secret my-test-key)
echo ${MCPGATEWAY_BEARER_TOKEN}
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/health | jq
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/tools | jq
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/version | jq
The mcpgateway-wrapper lets you connect to the gateway over stdio, while retaining authentication using the JWT token when the wrapper connect to a remote gateway. You should run this from a MCP client. You can test this from a shell with:
docker run -i --name mcpgateway-wrapper \
--entrypoint uv \
-e UV_CACHE_DIR=/tmp/uv-cache \
-e MCP_SERVER_CATALOG_URLS=http://host.docker.internal:4444 \
-e MCP_AUTH_TOKEN=$MCPGATEWAY_BEARER_TOKEN \
ghcr.io/ibm/mcp-context-forge:latest \
run --directory mcpgateway-wrapper mcpgateway-wrapper
# You'll see a message similar to: Installed 21 packages in 6ms - it's now expecting input from an MCP client
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.
# Run a time server, then register it in your gateway..
pip install mcp-server-time
npx -y supergateway --stdio "uvenv run mcp_server_time -- --local-timezone=Europe/Dublin"
# Start the MCP Gateway Wrapper
export MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1
python -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_current_time","arguments":{"timezone":"Europe/Dublin"}}}
Expected:
{"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.1.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":[{"name":"get_current_time","description":"Get current time in a specific timezones","inputSchema":{"type":"object","properties":{"timezone":{"type":"string","description":"IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."}},"required":["timezone"]}}]}}
# Running the time tool:
{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"{'content': [{'type': 'text', 'text': '{\\n \"timezone\": \"Europe/Dublin\",\\n \"datetime\": \"2025-06-08T21:47:07+01:00\",\\n \"is_dst\": true\\n}'}], 'is_error': False}"}],"isError":false}}
The mcpgateway-wrapper
should be used with an MCP Client that does not support SSE. You can configure it as such.
Remember to replace the MCP_SERVER_CATALOG_URL
with the actual URL of your MCP Gateway. Consider container networking - when running this via a container engine, this should represent a network accessible from Docker/Podman, ex: http://host.docker.internal:4444/servers/1
You have a number of options for running the wrapper. Docker/Podman, to run it from the container. uvx
, uvenv
or pipx
to run it straight from pip. Or just running it with Python from a local directory. Adjust your command accordingly.
{
"servers": {
"mcpgateway-wrapper": {
"command": "docker",
"args": [
"run",
"--rm",
"--network=host",
"-i",
"-e",
"MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1",
"-e",
"MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}",
"--entrypoint",
"uv",
"ghcr.io/ibm/mcp-context-forge:latest",
"run",
"--directory",
"mcpgateway-wrapper",
"mcpgateway-wrapper"
],
"env": {
"MCPGATEWAY_BEARER_TOKEN": "${MCPGATEWAY_BEARER_TOKEN}"
}
}
}
}
To add the mcpgateway to Claude Desktop (or similar MCP Clients) go to File > Settings > Developer > Edit Config
and add:
{
"mcpServers": {
"mcpgateway-wrapper": {
"command": "docker",
"args": [
"run",
"--rm",
"--network=host",
"-i",
"-e",
"MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1",
"-e",
"MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}",
"--entrypoint",
"uv",
"ghcr.io/ibm/mcp-context-forge:latest",
"run",
"--directory",
"mcpgateway-wrapper",
"mcpgateway-wrapper"
],
"env": {
"MCPGATEWAY_BEARER_TOKEN": "<place your token here>"
}
}
}
}
Restart Claude Desktop (exiting from system tray). Go back to File > Settings > Developer > Edit Config
to check on your configuration and view the logs.
For more details, see the Claude MCP quickstart. For issues, see MCP Debugging.
For the fastest development setup, use the provided VS Code Dev Container configuration. This provides a fully configured development environment with Python 3.11, Docker CLI, and all project dependencies pre-installed.
- VS Code with the Dev Containers extension
- Docker or Podman installed and running
-
Clone the repository:
git clone https://github.com/ibm/mcp-context-forge.git cd mcp-context-forge
-
Open in VS Code:
code .
-
Reopen in Container:
- VS Code will detect the
.devcontainer
configuration - Click "Reopen in Container" when prompted, or
- Use Command Palette (
Ctrl/Cmd+Shift+P
) β "Dev Containers: Reopen in Container"
- VS Code will detect the
-
Wait for setup:
- The container will build automatically (first time may take a few minutes)
- Development dependencies will be installed via
make install-dev
- Tests will run automatically to verify the setup
-
Start developing:
make dev # Start development server make test # Run tests make lint # Run linting
You can also use this project with GitHub Codespaces for cloud-based development:
- Click the "Code" button on the GitHub repository page
- Select "Codespaces" tab
- Click "Create codespace on main"
- Wait for the environment to be ready (same setup as local dev container)
The devcontainer includes:
- Python 3.11 with all project dependencies
- Docker CLI for container management
- VS Code extensions for Python and Docker development
- Pre-configured environment variables for development mode
- Automatic setup of
.env
file from.env.example
- Python β₯ 3.10
- GNU Make (optional, but all common workflows are available as Make targets)
- Optional: Docker / Podman for containerised runs
make venv install serve
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.)
You can use docker or podman, ex:
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
curl -k -sX GET \
-H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
https://localhost:4444/tools | jq
You should receive []
until you register a tool.
make venv install # create .venv + install deps
make serve # gunicorn on :4444
uv venv && source .venv/bin/activate
uv pip install -e '.[dev]' # IMPORTANT: in zsh, quote to disable glob expansion!
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
You can configure the gateway with SQLite, PostgreSQL (or any other compatible database) in .env.
When using PostgreSQL, you need to install psycopg2
driver.
uv pip install psycopg2-binary # dev convenience
# or
uv pip install psycopg2 # production build
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.
β οΈ 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.examples
to .env
and making the necessary edits to fit your environment.
Setting | Description | Default | Options |
---|---|---|---|
APP_NAME |
Gateway / OpenAPI title | MCP Gateway |
string |
HOST |
Bind address for the app | 0.0.0.0 |
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 likehttps://host.com/foo/
.
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: Bearer <token>
)Generate tokens via:
python -m mcpgateway.utils.create_jwt_token -u admin -e 10080 > token.txt export MCPGATEWAY_BEARER_TOKEN=$(cat token.txt)Tokens allow non-interactive API clients to authenticate securely.
π§ͺ Set
AUTH_REQUIRED=false
during development if you want to disable all authentication (e.g. for local testing or open APIs) or clients that don't support SSE authentication. In production, you should use the SSE to stdiomcpgateway-wrapper
for such tools that don't support authenticated SSE, while still ensuring the gateway uses authentication.π
AUTH_ENCRYPTION_SECRET
is used to encrypt and decrypt tool authentication credentials (auth_value
). You must set the same value across environments to decode previously stored encrypted auth values. Recommended: use a long, random string.
Setting | Description | Default | Options |
---|---|---|---|
MCPGATEWAY_UI_ENABLED |
Enable the interactive Admin dashboard | true |
bool |
MCPGATEWAY_ADMIN_API_ENABLED |
Enable API endpoints for admin ops | true |
bool |
π₯οΈ Set both to
false
to disable management UI and APIs in production.
Setting | Description | Default | Options |
---|---|---|---|
SKIP_SSL_VERIFY |
Skip upstream TLS verification | false |
bool |
ALLOWED_ORIGINS |
CORS allowβlist | ["http://localhost","http://localhost:4444"] |
JSON array |
CORS_ENABLED |
Enable CORS | true |
bool |
Note: do not quote the ALLOWED_ORIGINS values, this needs to be valid JSON, such as:
ALLOWED_ORIGINS=["http://localhost", "http://localhost:4444"]
Setting | Description | Default | Options |
---|---|---|---|
LOG_LEVEL |
Minimum log level | INFO |
DEBUG β¦CRITICAL |
LOG_FORMAT |
Log format | json |
json , text |
LOG_FILE |
Log output file | (none) | path or empty |
Setting | Description | Default | Options |
---|---|---|---|
TRANSPORT_TYPE |
Enabled transports | all |
http ,ws ,sse ,stdio ,all |
WEBSOCKET_PING_INTERVAL |
WebSocket ping (secs) | 30 |
int > 0 |
SSE_RETRY_TIMEOUT |
SSE retry timeout (ms) | 5000 |
int > 0 |
Setting | Description | Default | Options |
---|---|---|---|
FEDERATION_ENABLED |
Enable federation | true |
bool |
FEDERATION_DISCOVERY |
Autoβdiscover peers | false |
bool |
FEDERATION_PEERS |
Comma-sep peer URLs | [] |
JSON array |
FEDERATION_TIMEOUT |
Gateway timeout (secs) | 30 |
int > 0 |
FEDERATION_SYNC_INTERVAL |
Sync interval (secs) | 300 |
int > 0 |
Setting | Description | Default | Options |
---|---|---|---|
RESOURCE_CACHE_SIZE |
LRU cache size | 1000 |
int > 0 |
RESOURCE_CACHE_TTL |
Cache TTL (seconds) | 3600 |
int > 0 |
MAX_RESOURCE_SIZE |
Max resource bytes | 10485760 |
int > 0 |
ALLOWED_MIME_TYPES |
Acceptable MIME types | see code | JSON array |
Setting | Description | Default | Options |
---|---|---|---|
TOOL_TIMEOUT |
Tool invocation timeout (secs) | 60 |
int > 0 |
MAX_TOOL_RETRIES |
Max retry attempts | 3 |
int β₯ 0 |
TOOL_RATE_LIMIT |
Tool calls per minute | 100 |
int > 0 |
TOOL_CONCURRENT_LIMIT |
Concurrent tool invocations | 10 |
int > 0 |
Setting | Description | Default | Options |
---|---|---|---|
PROMPT_CACHE_SIZE |
Cached prompt templates | 100 |
int > 0 |
MAX_PROMPT_SIZE |
Max prompt template size (bytes) | 102400 |
int > 0 |
PROMPT_RENDER_TIMEOUT |
Jinja render timeout (secs) | 10 |
int > 0 |
Setting | Description | Default | Options |
---|---|---|---|
HEALTH_CHECK_INTERVAL |
Health poll interval (secs) | 60 |
int > 0 |
HEALTH_CHECK_TIMEOUT |
Health request timeout (secs) | 10 |
int > 0 |
UNHEALTHY_THRESHOLD |
Fail-count before peer deactivation, | 3 |
int > 0 |
Set to -1 if deactivation is not needed. |
Setting | Description | Default | Options |
---|---|---|---|
DB_POOL_SIZE |
SQLAlchemy connection pool size | 200 |
int > 0 |
DB_MAX_OVERFLOW |
Extra connections beyond pool | 10 |
int β₯ 0 |
DB_POOL_TIMEOUT |
Wait for connection (secs) | 30 |
int > 0 |
DB_POOL_RECYCLE |
Recycle connections (secs) | 3600 |
int > 0 |
Setting | Description | Default | Options |
---|---|---|---|
CACHE_TYPE |
Backend (memory /redis ) |
memory |
none , memory ,redis |
REDIS_URL |
Redis connection URL | (none) | string or empty |
CACHE_PREFIX |
Key prefix | mcpgw: |
string |
π§
none
disables caching entirely. Usememory
for dev,database
for persistence, orredis
for distributed caching.
Setting | Description | Default | Options |
---|---|---|---|
DEV_MODE |
Enable dev mode | false |
bool |
RELOAD |
Auto-reload on changes | false |
bool |
DEBUG |
Debug logging | false |
bool |
make serve # Run production Gunicorn server on
make serve-ssl # Run Gunicorn behind HTTPS on :4444 (uses ./certs)
To run the development (uvicorn) server:
make dev
# or
./run.sh --reload --log debug --workers 2
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 |
uvicorn mcpgateway.main:app --host 0.0.0.0 --port 4444 --workers 4
# Generate a JWT token using JWT_SECRET_KEY and export it as MCPGATEWAY_BEARER_TOKEN
export MCPGATEWAY_BEARER_TOKEN=$(JWT_SECRET_KEY=my-test-key python3 mcpgateway/utils/create_jwt_token.py)
# Use the JWT token in an API call
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/tools
Deployment details can be found in the GitHub Pages.
This project supports deployment to IBM Cloud Code Engine using the ibmcloud CLI and the IBM Container Registry.
- Podman or Docker installed locally
- IBM Cloud CLI (use
make ibmcloud-cli-install
to install) - An IBM Cloud API key with access to Code Engine & Container Registry
- Code Engine and Container Registry services enabled in your IBM Cloud account
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
β Quick check:
make ibmcloud-check-env
Target | Purpose |
---|---|
make ibmcloud-cli-install |
Install IBM Cloud CLI and required plugins |
make ibmcloud-login |
Log in to IBM Cloud (API key or SSO) |
make ibmcloud-ce-login |
Select the Code Engine project & region |
make ibmcloud-tag |
Tag the local container image |
make ibmcloud-push |
Push the image to IBM Container Registry |
make ibmcloud-deploy |
Create or update the Code Engine application (uses CPU/memory/secret) |
make ibmcloud-ce-status |
Show current deployment status |
make ibmcloud-ce-logs |
Stream logs from the running app |
make ibmcloud-ce-rm |
Delete the Code Engine application |
make ibmcloud-check-env
make ibmcloud-cli-install
make ibmcloud-login
make ibmcloud-ce-login
make ibmcloud-tag
make ibmcloud-push
make ibmcloud-deploy
make ibmcloud-ce-status
make ibmcloud-ce-logs
Generate an API Bearer token, and test the various API endpoints:
# Generate a bearer token using the configured secret key (use the same as your .env)
export MCPGATEWAY_BEARER_TOKEN=$(python -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
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
# 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
# Generic JSON-RPC calls (tools, gateways, roots, etc.)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"list_tools"}' \
http://localhost:4444/rpc
Handles any method name: list_tools
, list_gateways
, prompts/get
, or invokes a tool if method matches a registered tool name .
# 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
# 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
# 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
# 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
# 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
# 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/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/1
# Toggle active
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/servers/1/toggle?activate=false
# 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
# SSE: all events
curl -N -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/events
# WebSocket
wscat -c ws://localhost:4444/ws \
-H "Authorization: Basic $(echo -n admin:changeme|base64)"
# Health check
curl http://localhost:4444/health
Full Swagger UI at /docs
.
uvicorn sample_tool.clock_tool:app --host 0.0.0.0 --port 9000
curl -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"get_time","params":{"timezone":"UTC"}}' \
http://localhost:9000/rpc
make test # Run unit tests
make lint # Run lint tools
# ββββββββββ 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
β β βββ manager.py # Orchestration & health checks
β βββ 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
β βββ types.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
- Swagger UI β http://localhost:4444/docs
- ReDoc β http://localhost:4444/redoc
- Admin Panel β http://localhost:4444/admin
This project offer the following Makefile targets. Type make
in the project root to show all 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 containerised Sonar Scanner CLI with Docker
sonar-submit-podman - Run containerised 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
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 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
- 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.
A complete changelog can be found here: CHANGELOG.md
Licensed under the Apache License 2.0 β see LICENSE
- Mihai Criveti - Distinguished Engineer, Agentic AI
Special thanks to our contributors for helping us improve ContextForge MCP Gateway:
PyPi Downloads: