# MCP Gateway
> Model Context Protocol gateway & proxy - unify REST, MCP, and A2A with federation, virtual servers, retries, security, and an optional admin UI.

<!-- === CI / Security / Build Badges === -->
[](https://github.com/IBM/mcp-context-forge/actions/workflows/python-package.yml)
[](https://github.com/IBM/mcp-context-forge/actions/workflows/codeql.yml)
[](https://github.com/IBM/mcp-context-forge/actions/workflows/bandit.yml)
[](https://github.com/IBM/mcp-context-forge/actions/workflows/dependency-review.yml)
[](https://github.com/IBM/mcp-context-forge/actions/workflows/pytest.yml)
[](https://github.com/IBM/mcp-context-forge/actions/workflows/lint.yml)
<!-- === Container Build & Deploy === -->
[](https://github.com/IBM/mcp-context-forge/actions/workflows/docker-image.yml)
[](https://github.com/IBM/mcp-context-forge/actions/workflows/ibm-cloud-code-engine.yml)
<!-- === Package / Container === -->
[](https://docs.python.org/3/library/asyncio.html)
[](LICENSE)
[](https://pypi.org/project/mcp-contextforge-gateway/)
[](https://github.com/ibm/mcp-context-forge/pkgs/container/mcp-context-forge)
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.

---
## Table of Contents
<!-- vscode-markdown-toc -->
## Table of Contents
* 1. [Table of Contents](#table-of-contents)
* 2. [๐ Overview & Goals](#-overview--goals)
* 3. [Quick Start - PyPI](#quick-start---pypi)
* 3.1. [1 - Install & run (copy-paste friendly)](#1---install--run-copy-paste-friendly)
* 4. [Quick Start - Containers](#quick-start---containers)
* 4.1. [๐ณ Docker](#-docker)
* 4.1.1. [1 - Minimum viable run](#1---minimum-viable-run)
* 4.1.2. [2 - Persist the SQLite database](#2---persist-the-sqlite-database)
* 4.1.3. [3 - Local tool discovery (host network)](#3---local-tool-discovery-host-network)
* 4.2. [๐ฆญ Podman (rootless-friendly)](#-podman-rootless-friendly)
* 4.2.1. [1 - Basic run](#1---basic-run)
* 4.2.2. [2 - Persist SQLite](#2---persist-sqlite)
* 4.2.3. [3 - Host networking (rootless)](#3---host-networking-rootless)
* 5. [Testing `mcpgateway.wrapper` by hand](#testing-mcpgatewaywrapper-by-hand)
* 5.1. [๐งฉ Running from an MCP Client (`mcpgateway.wrapper`)](#-running-from-an-mcp-client-mcpgatewaywrapper)
* 5.1.1. [1 - Install `uv` (`uvx` is an alias it provides)](#1---install-uv-uvx-is-an-alias-it-provides)
* 5.1.2. [2 - Create an on-the-spot venv & run the wrapper](#2---create-an-on-the-spot-venv--run-the-wrapper)
* 5.1.3. [Claude Desktop JSON (runs through **uvx**)](#claude-desktop-json-runs-through-uvx)
* 5.2. [๐ Using with Claude Desktop (or any GUI MCP client)](#-using-with-claude-desktop-or-any-gui-mcp-client)
* 6. [๐ Quick Start: VS Code Dev Container](#-quick-start-vs-code-dev-container)
* 6.1. [1 - Clone & Open](#1---clone--open)
* 6.2. [2 - First-Time Build (Automatic)](#2---first-time-build-automatic)
* 7. [Quick Start (manual install)](#quick-start-manual-install)
* 7.1. [Prerequisites](#prerequisites)
* 7.2. [One-liner (dev)](#one-liner-dev)
* 7.3. [Containerised (self-signed TLS)](#containerised-self-signed-tls)
* 7.4. [Smoke-test the API](#smoke-test-the-api)
* 8. [Installation](#installation)
* 8.1. [Via Make](#via-make)
* 8.2. [UV (alternative)](#uv-alternative)
* 8.3. [pip (alternative)](#pip-alternative)
* 8.4. [Optional (PostgreSQL adapter)](#optional-postgresql-adapter)
* 8.4.1. [Quick Postgres container](#quick-postgres-container)
* 9. [Configuration (`.env` or env vars)](#configuration-env-or-env-vars)
* 9.1. [Basic](#basic)
* 9.2. [Authentication](#authentication)
* 9.3. [UI Features](#ui-features)
* 9.4. [Security](#security)
* 9.5. [Logging](#logging)
* 9.6. [Transport](#transport)
* 9.7. [Federation](#federation)
* 9.8. [Resources](#resources)
* 9.9. [Tools](#tools)
* 9.10. [Prompts](#prompts)
* 9.11. [Health Checks](#health-checks)
* 9.12. [Database](#database)
* 9.13. [Cache Backend](#cache-backend)
* 9.14. [Development](#development)
* 10. [Running](#running)
* 10.1. [Makefile](#makefile)
* 10.2. [Script helper](#script-helper)
* 10.3. [Manual (Uvicorn)](#manual-uvicorn)
* 11. [Authentication examples](#authentication-examples)
* 12. [โ๏ธ AWS / Azure / OpenShift](#๏ธ-aws--azure--openshift)
* 13. [โ๏ธ IBM Cloud Code Engine Deployment](#๏ธ-ibm-cloud-code-engine-deployment)
* 13.1. [๐ง Prerequisites](#-prerequisites-1)
* 13.2. [๐ฆ Environment Variables](#-environment-variables)
* 13.3. [๐ Make Targets](#-make-targets)
* 13.4. [๐ Example Workflow](#-example-workflow)
* 14. [API Endpoints](#api-endpoints)
* 15. [Testing](#testing)
* 16. [Project Structure](#project-structure)
* 17. [API Documentation](#api-documentation)
* 18. [Makefile targets](#makefile-targets)
* 19. [๐ Troubleshooting](#-troubleshooting)
* 19.1. [Diagnose the listener](#diagnose-the-listener)
* 19.2. [Why localhost fails on Windows](#why-localhost-fails-on-windows)
* 19.2.1. [Fix (Podman rootless)](#fix-podman-rootless)
* 19.2.2. [Fix (Docker Desktop > 4.19)](#fix-docker-desktop--419)
* 20. [Contributing](#contributing)
* 21. [Changelog](#changelog)
* 22. [License](#license)
* 23. [Core Authors and Maintainers](#core-authors-and-maintainers)
* 24. [Star History and Project Activity](#star-history-and-project-activity)
<!-- vscode-markdown-toc-config
numbering=true
autoSave=true
/vscode-markdown-toc-config -->
<!-- /vscode-markdown-toc -->
## ๐ Overview & Goals
**ContextForge MCP Gateway** is a production-grade gateway, registry, and proxy that sits in front of any [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server or REST API-exposing a unified endpoint for all your AI clients.
It supports:
* Federation across multiple MCP and REST services
* Virtualization of legacy APIs as MCP-compliant tools and servers
* Transport over HTTP, JSON-RPC, WebSocket, SSE, stdio and streamable-HTTP
* An Admin UI for real-time management and configuration
* Built-in auth, observability, retries, and rate-limiting
* 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](https://ibm.github.io/mcp-context-forge/architecture/roadmap/)
---
<details>
<summary><strong>๐ Gateway Layer with Protocol Flexibility</strong></summary>
* 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
</details>
<details>
<summary><strong>๐ Federation of Peer Gateways (MCP Registry)</strong></summary>
* 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
</details>
<details>
<summary><strong>๐งฉ Virtualization of REST/gRPC Services</strong></summary>
* Wraps non-MCP services as virtual MCP servers
* Registers tools, prompts, and resources with minimal configuration
</details>
<details>
<summary><strong>๐ REST-to-MCP Tool Adapter</strong></summary>
* Adapts REST APIs into tools with:
* Automatic JSON Schema extraction
* Support for headers, tokens, and custom auth
* Retry, timeout, and rate-limit policies
</details>
<details>
<summary><strong>๐ง Unified Registries</strong></summary>
* **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
</details>
<details>
<summary><strong>๐ Admin UI, Observability & Dev Experience</strong></summary>
* Admin UI built with HTMX + Alpine.js
* Auth: Basic, JWT, or custom schemes
* Structured logs, health endpoints, metrics
* 400+ tests, Makefile targets, live reload, pre-commit hooks
</details>
---
## Quick Start - PyPI
MCP Gateway is published on [PyPI](https://pypi.org/project/mcp-contextforge-gateway/) as `mcp-contextforge-gateway`.
---
<details>
<summary><strong>๐ Prerequisites</strong></summary>
* **Python โฅ 3.10** (3.11 recommended)
* **curl + jq** - only for the last smoke-test step
</details>
### 1 - Install & run (copy-paste friendly)
```bash
# 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
```
<details>
<summary><strong>More configuration</strong></summary>
Copy [.env.example](.env.example) to `.env` and tweak any of the settings (or use them as env variables).
</details>
<details>
<summary><strong>๐ End-to-end demo (register a local MCP server)</strong></summary>
```bash
# 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" \
--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" --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" --port 8002
# 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
```
</details>
<details>
<summary><strong>๐ง Using the stdio wrapper (mcpgateway-wrapper)</strong></summary>
```bash
export MCP_AUTH_TOKEN=$MCPGATEWAY_BEARER_TOKEN
export MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/UUID_OF_SERVER_1
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_TOKEN` and `MCP_SERVER_CATALOG_URLS` env variables, and select `python3` as the Command, and `-m mcpgateway.wrapper` as Arguments.
```bash
echo $PWD/.venv/bin/python3 # Using the Python3 full path ensures you have a working venv
export MCP_SERVER_CATALOG_URLS='http://localhost:4444/servers/UUID_OF_SERVER_1'
export MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}
npx -y @modelcontextprotocol/inspector
```
When using a MCP Client such as Claude with stdio:
```json
{
"mcpServers": {
"mcpgateway-wrapper": {
"command": "python",
"args": ["-m", "mcpgateway.wrapper"],
"env": {
"MCP_AUTH_TOKEN": "your-token-here",
"MCP_SERVER_CATALOG_URLS": "http://localhost:4444/servers/UUID_OF_SERVER_1",
"MCP_TOOL_CALL_TIMEOUT": "120"
}
}
}
}
```
</details>
---
## Quick Start - Containers
Use the official OCI image from GHCR with **Docker** *or* **Podman**.
---
### ๐ณ Docker
#### 1 - Minimum viable run
```bash
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:0.3.1
# 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.3.1 \
python -m mcpgateway.utils.create_jwt_token --username admin --exp 0 --secret my-test-key
```
Browse to **[http://localhost:4444/admin](http://localhost:4444/admin)** (user `admin` / pass `changeme`).
#### 2 - Persist the SQLite database
```bash
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 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.3.1
```
SQLite now lives on the host at `./data/mcp.db`.
#### 3 - Local tool discovery (host network)
```bash
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 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.3.1
```
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](https://docs.docker.com/engine/network/drivers/host/) for more details.
---
### ๐ฆญ Podman (rootless-friendly)
#### 1 - Basic run
```bash
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.3.1
```
#### 2 - Persist SQLite
```bash
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.3.1
```
#### 3 - Host networking (rootless)
```bash
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.3.1
```
---
<details>
<summary><strong>โ๏ธ Docker/Podman tips</strong></summary>
* **.env files** - Put all the `-e FOO=` lines into a file and replace them with `--env-file .env`. See the provided [.env.example](.env.example) for reference.
* **Pinned tags** - Use an explicit version (e.g. `v0.3.1`) instead of `latest` for reproducible builds.
* **JWT tokens** - Generate one in the running container:
```bash
docker exec mcpgateway python3 -m mcpgateway.utils.create_jwt_token -u admin -e 10080 --secret my-test-key
```
* **Upgrades** - Stop, remove, and rerun with the same `-v $(pwd)/data:/data` mount; your DB and config stay intact.
</details>
---
<details>
<summary><strong>๐ Smoke-test the running container</strong></summary>
```bash
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
```
</details>
---
<details>
<summary><strong>๐ง Running the MCP Gateway stdio wrapper</strong></summary>
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.
```bash
# 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_TOKEN=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_CATALOG_URLS='http://localhost:4444/servers/UUID_OF_SERVER_1'
export MCP_TOOL_CALL_TIMEOUT=120
export MCP_WRAPPER_LOG_LEVEL=DEBUG # or OFF to disable logging
docker run --rm -i \
-e MCP_AUTH_TOKEN=$MCPGATEWAY_BEARER_TOKEN \
-e MCP_SERVER_CATALOG_URLS=http://host.docker.internal:4444/servers/UUID_OF_SERVER_1 \
-e MCP_TOOL_CALL_TIMEOUT=120 \
-e MCP_WRAPPER_LOG_LEVEL=DEBUG \
ghcr.io/ibm/mcp-context-forge:0.3.1 \
python3 -m mcpgateway.wrapper
```
</details>
---
## 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.
```bash
# Start the MCP Gateway Wrapper
export MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/YOUR_SERVER_UUID
python3 -m mcpgateway.wrapper
```
<details>
<summary><strong>Initialize the protocol</strong></summary>
```json
# 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"}}}
```
</details>
<details>
<summary><strong>Expected responses from mcpgateway.wrapper</strong></summary>
```json
{"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.3.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"}]}}
```
</details>
### ๐งฉ 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`.
> When inside Docker/Podman, that often becomes `http://host.docker.internal:4444/servers/UUID_OF_SERVER_1` (macOS/Windows) or the gateway container's hostname (Linux).
---
<details>
<summary><strong>๐ณ Docker / Podman</strong></summary>
```bash
docker run -i --rm \
--network=host \
-e MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/UUID_OF_SERVER_1 \
-e MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \
-e MCP_TOOL_CALL_TIMEOUT=120 \
ghcr.io/ibm/mcp-context-forge:0.3.1 \
python3 -m mcpgateway.wrapper
```
</details>
---
<details>
<summary><strong>๐ฆ pipx (one-liner install & run)</strong></summary>
```bash
# Install gateway package in its own isolated venv
pipx install --include-deps mcp-contextforge-gateway
# Run the stdio wrapper
MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \
MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/UUID_OF_SERVER_1 \
python3 -m mcpgateway.wrapper
# Alternatively with uv
uv run --directory . -m mcpgateway.wrapper
```
**Claude Desktop JSON** (uses the host Python that pipx injected):
```json
{
"mcpServers": {
"mcpgateway-wrapper": {
"command": "python3",
"args": ["-m", "mcpgateway.wrapper"],
"env": {
"MCP_AUTH_TOKEN": "<your-token>",
"MCP_SERVER_CATALOG_URLS": "http://localhost:4444/servers/UUID_OF_SERVER_1",
"MCP_TOOL_CALL_TIMEOUT": "120"
}
}
}
}
```
</details>
---
<details>
<summary><strong>โก uv / uvx (light-speed venvs)</strong></summary>
#### 1 - Install <code>uv</code> (<code>uvx</code> is an alias it provides)
```bash
# (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
```bash
# 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_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \
MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/UUID_OF_SERVER_1 \
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**)
```json
{
"mcpServers": {
"mcpgateway-wrapper": {
"command": "uvx",
"args": [
"run",
"--",
"python",
"-m",
"mcpgateway.wrapper"
],
"env": {
"MCP_AUTH_TOKEN": "<your-token>",
"MCP_SERVER_CATALOG_URLS": "http://localhost:4444/servers/UUID_OF_SERVER_1"
}
}
}
```
</details>
---
### ๐ Using with Claude Desktop (or any GUI MCP client)
1. **Edit Config** โ `File โธ Settings โธ Developer โธ Edit Config`
2. Paste one of the JSON blocks above (Docker / pipx / uvx).
3. Restart the app so the new stdio server is spawned.
4. 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](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.
---
<details>
<summary><strong>๐ Prerequisites</strong></summary>
* **VS Code** with the [Dev Containers extension](https://code.visualstudio.com/docs/devcontainers/containers)
* **Docker** or **Podman** installed and running locally
</details>
<details>
<summary><strong>๐งฐ Setup Instructions</strong></summary>
### 1 - Clone & Open
```bash
git clone https://github.com/ibm/mcp-context-forge.git
cd mcp-context-forge
code .
```
VS Code will detect the `.devcontainer` and prompt:
**"Reopen in Container"**
*or* manually run: <kbd>Ctrl/Cmd โง P</kbd> โ **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.
</details>
<details>
<summary><strong>๐ ๏ธ Daily Developer Workflow</strong></summary>
Common tasks inside the container:
```bash
# Start dev server (hot reload)
make dev # http://localhost:4444
# Run tests & linters
make test
make lint
```
Optional:
* `make bash` - drop into an interactive shell
* `make clean` - clear build artefacts & caches
* Port forwarding is automatic (customize via `.devcontainer/devcontainer.json`)
</details>
<details>
<summary><strong>โ๏ธ GitHub Codespaces: 1-Click Cloud IDE</strong></summary>
No local Docker? Use Codespaces:
1. Go to the repo โ **Code โธ Codespaces โธ Create codespace on main**
2. Wait for the container image to build in the cloud
3. Develop using the same workflow above
</details>
---
## Quick Start (manual install)
### Prerequisites
* **Python โฅ 3.10**
* **GNU Make** (optional, but all common workflows are available as Make targets)
* Optional: **Docker / Podman** for containerised runs
### One-liner (dev)
```bash
make venv install serve
```
What it does:
1. Creates / activates a `.venv` in your home folder `~/.venv/mcpgateway`
2. Installs the gateway and necessary dependencies
3. Launches **Gunicorn** (Uvicorn workers) on [http://localhost:4444](http://localhost:4444)
For development, you can use:
```bash
make install-dev # Install development dependencies, ex: linters and test harness
make lint # optional: run style checks (ruff, mypy, etc.)
```
### Containerised (self-signed TLS)
> 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
```bash
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
```bash
make venv install # create .venv + install deps
make serve # gunicorn on :4444
```
### UV (alternative)
```bash
uv venv && source .venv/bin/activate
uv pip install -e '.[dev]' # IMPORTANT: in zsh, quote to disable glob expansion!
```
### pip (alternative)
```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
```
### 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.
```bash
uv pip install psycopg2-binary # dev convenience
# or
uv pip install psycopg2 # production build
```
#### Quick Postgres container
```bash
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](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](.env.example) to `.env` and making the necessary edits to fit your environment.
<details>
<summary><strong>๐ง Environment Configuration Variables</strong></summary>
### Basic
| 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 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: Bearer <token>`)
> * Generate tokens via:
>
> ```bash
> export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 0 --secret my-test-key)
> echo $MCPGATEWAY_BEARER_TOKEN
> ```
> * 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 stdio `mcpgateway-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.
### UI Features
| 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.
### Security
| 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"]`
### Logging
| 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 |
### Transport
| 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 |
| `USE_STATEFUL_SESSIONS` | streamable http config | `false` | bool |
| `JSON_RESPONSE_ENABLED` | json/sse streams (streamable http) | `true` | bool |
### Federation
| 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 |
### Resources
| 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 |
### Tools
| 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 |
### Prompts
| 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 |
### Health Checks
| 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. | | |
### Database
| 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 |
| `DB_MAX_RETRIES` . | Max Retry Attempts | `3` | int > 0 |
| `DB_RETRY_INTERVAL_MS` | Retry Interval (ms) | `2000` | int > 0 |
### Cache Backend
| 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 |
| `REDIS_MAX_RETRIES` | Max Retry Attempts | `3` | int > 0 |
| `REDIS_RETRY_INTERVAL_MS` | Retry Interval (ms) | `2000` | int > 0 |
> ๐ง `none` disables caching entirely. Use `memory` for dev, `database` for persistence, or `redis` for distributed caching.
### Development
| Setting | Description | Default | Options |
| ---------- | ---------------------- | ------- | ------- |
| `DEV_MODE` | Enable dev mode | `false` | bool |
| `RELOAD` | Auto-reload on changes | `false` | bool |
| `DEBUG` | Debug logging | `false` | bool |
</details>
---
## Running
### Makefile
```bash
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:
```bash
make dev
# or
./run.sh --reload --log debug --workers 2
```
> `run.sh` is a wrapper around `uvicorn` 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)
```bash
uvicorn mcpgateway.main:app --host 0.0.0.0 --port 4444 --workers 4
```
---
## Authentication examples
```bash
# 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](https://cloud.ibm.com/codeengine) using the **ibmcloud** CLI and the IBM Container Registry.
<details>
<summary><strong>โ๏ธ IBM Cloud Code Engine Deployment</strong></summary>
### ๐ง 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:
```bash
# โโ 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`
---
### ๐ Make Targets
| 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 |
---
### ๐ Example Workflow
```bash
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
```
</details>
---
## 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](http://localhost:4444/docs)
* **ReDoc** โ [http://localhost:4444/redoc](http://localhost:4444/redoc)
Generate an API Bearer token, and test the various API endpoints.
<details>
<summary><strong>๐ Authentication & Health Checks</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐งฑ Protocol APIs (MCP) /protocol</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐ง JSON-RPC Utility /rpc</strong></summary>
```bash
# 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 .
</details>
---
<details>
<summary><strong>๐ง Tool Management /tools</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐ Gateway Management /gateways</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐ Resource Management /resources</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐ Prompt Management /prompts</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐ฒ Root Management /roots</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐ฅ๏ธ Server Management /servers</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐ Metrics /metrics</strong></summary>
```bash
# 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
```
</details>
---
<details>
<summary><strong>๐ก Events & Health</strong></summary>
```bash
# 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`.
</details>
---
<details>
<summary><strong>๐ ๏ธ Sample Tool</strong></summary>
```bash
uvicorn sample_tool.clock_tool:app --host 0.0.0.0 --port 9000
```
```bash
curl -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"get_time","params":{"timezone":"UTC"}}' \
http://localhost:9000/rpc
```
</details>
---
## Testing
```bash
make test # Run unit tests
make lint # Run lint tools
```
---
## Project Structure
<details>
<summary><strong>๐ Directory and file structure for mcpgateway</strong></summary>
```bash
# โโโโโโโโโโ 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
```
</details>
---
## API Documentation
* **Swagger UI** โ [http://localhost:4444/docs](http://localhost:4444/docs)
* **ReDoc** โ [http://localhost:4444/redoc](http://localhost:4444/redoc)
* **Admin Panel** โ [http://localhost:4444/admin](http://localhost:4444/admin)
---
## Makefile targets
This project offer the following Makefile targets. Type `make` in the project root to show all targets.
<details>
<summary><strong>๐ง Available Makefile targets</strong></summary>
```bash
๐ 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
```
</details>
## ๐ Troubleshooting
<details>
<summary><strong>Port publishing on WSL2 (rootless Podman & Docker Desktop)</strong></summary>
### Diagnose the listener
```bash
# Inside your WSL distro
ss -tlnp | grep 4444 # Use ss
netstat -anp | grep 4444 # or netstat
```
*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)
```bash
# Inside the WSL distro
echo "wsl" | sudo tee /etc/containers/podman-machine
systemctl --user restart podman.socket
```
`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:
```bash
docker restart mcpgateway
```
</details>
<details>
<summary><strong>Gateway starts but immediately exits ("Failed to read DATABASE_URL")</strong></summary>
Copy `.env.example` to `.env` first:
```bash
cp .env.example .env
```
Then edit `DATABASE_URL`, `JWT_SECRET_KEY`, `BASIC_AUTH_PASSWORD`, etc.
Missing or empty required vars cause a fast-fail at startup.
</details>
## Contributing
1. Fork the repo, create a feature branch.
2. Run `make lint` and fix any issues.
3. Keep `make test` green and 100% coverage.
4. Open a PR - describe your changes clearly.
See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
---
## Changelog
A complete changelog can be found here: [CHANGELOG.md](./CHANGELOG.md)
## License
Licensed under the **Apache License 2.0** - see [LICENSE](./LICENSE)
## Core Authors and Maintainers
- [Mihai Criveti](https://www.linkedin.com/in/crivetimihai) - Distinguished Engineer, Agentic AI
Special thanks to our contributors for helping us improve ContextForge MCP Gateway:
<a href="https://github.com/ibm/mcp-context-forge/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ibm/mcp-context-forge&max=100&anon=0&columns=10" />
</a>
## Star History and Project Activity
[](https://www.star-history.com/#ibm/mcp-context-forge&Date)
<!-- === Usage Stats === -->
[](https://pepy.tech/project/mcp-contextforge-gateway)
[](https://github.com/ibm/mcp-context-forge/stargazers)
[](https://github.com/ibm/mcp-context-forge/network/members)
[](https://github.com/ibm/mcp-context-forge/graphs/contributors)
[](https://github.com/ibm/mcp-context-forge/commits)
[](https://github.com/ibm/mcp-context-forge/issues)
Raw data
{
"_id": null,
"home_page": null,
"name": "mcp-contextforge-gateway",
"maintainer": null,
"docs_url": null,
"requires_python": "<3.14,>=3.11",
"maintainer_email": "Mihai Criveti <redacted@ibm.com>",
"keywords": "MCP, API, gateway, proxy, tools, agents, agentic ai, model context protocol, multi-agent, fastapi, json-rpc, sse, websocket, federation, security, authentication",
"author": null,
"author_email": "Mihai Criveti <redacted@ibm.com>",
"download_url": "https://files.pythonhosted.org/packages/e1/1e/8a30fb7a476dc481d4c9d99d1a574d80bcd5b40bb75a5f4643ad82d0d8d4/mcp_contextforge_gateway-0.3.1.tar.gz",
"platform": null,
"description": "# MCP Gateway\n\n> Model Context Protocol gateway & proxy - unify REST, MCP, and A2A with federation, virtual servers, retries, security, and an optional admin UI.\n\n\n\n<!-- === CI / Security / Build Badges === -->\n[](https://github.com/IBM/mcp-context-forge/actions/workflows/python-package.yml) \n[](https://github.com/IBM/mcp-context-forge/actions/workflows/codeql.yml) \n[](https://github.com/IBM/mcp-context-forge/actions/workflows/bandit.yml) \n[](https://github.com/IBM/mcp-context-forge/actions/workflows/dependency-review.yml) \n[](https://github.com/IBM/mcp-context-forge/actions/workflows/pytest.yml) \n[](https://github.com/IBM/mcp-context-forge/actions/workflows/lint.yml)\n\n<!-- === Container Build & Deploy === -->\n[](https://github.com/IBM/mcp-context-forge/actions/workflows/docker-image.yml) \n[](https://github.com/IBM/mcp-context-forge/actions/workflows/ibm-cloud-code-engine.yml)\n\n<!-- === Package / Container === -->\n[](https://docs.python.org/3/library/asyncio.html)\n[](LICENSE) \n[](https://pypi.org/project/mcp-contextforge-gateway/) \n[](https://github.com/ibm/mcp-context-forge/pkgs/container/mcp-context-forge) \n\n\nContextForge 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.\n\n\n---\n\n## Table of Contents\n\n<!-- vscode-markdown-toc -->\n## Table of Contents\n\n* 1. [Table of Contents](#table-of-contents)\n* 2. [\ud83d\ude80 Overview & Goals](#-overview--goals)\n* 3. [Quick Start - PyPI](#quick-start---pypi)\n * 3.1. [1 - Install & run (copy-paste friendly)](#1---install--run-copy-paste-friendly)\n* 4. [Quick Start - Containers](#quick-start---containers)\n * 4.1. [\ud83d\udc33 Docker](#-docker)\n * 4.1.1. [1 - Minimum viable run](#1---minimum-viable-run)\n * 4.1.2. [2 - Persist the SQLite database](#2---persist-the-sqlite-database)\n * 4.1.3. [3 - Local tool discovery (host network)](#3---local-tool-discovery-host-network)\n * 4.2. [\ud83e\uddad Podman (rootless-friendly)](#-podman-rootless-friendly)\n * 4.2.1. [1 - Basic run](#1---basic-run)\n * 4.2.2. [2 - Persist SQLite](#2---persist-sqlite)\n * 4.2.3. [3 - Host networking (rootless)](#3---host-networking-rootless)\n* 5. [Testing `mcpgateway.wrapper` by hand](#testing-mcpgatewaywrapper-by-hand)\n * 5.1. [\ud83e\udde9 Running from an MCP Client (`mcpgateway.wrapper`)](#-running-from-an-mcp-client-mcpgatewaywrapper)\n * 5.1.1. [1 - Install `uv` (`uvx` is an alias it provides)](#1---install-uv-uvx-is-an-alias-it-provides)\n * 5.1.2. [2 - Create an on-the-spot venv & run the wrapper](#2---create-an-on-the-spot-venv--run-the-wrapper)\n * 5.1.3. [Claude Desktop JSON (runs through **uvx**)](#claude-desktop-json-runs-through-uvx)\n * 5.2. [\ud83d\ude80 Using with Claude Desktop (or any GUI MCP client)](#-using-with-claude-desktop-or-any-gui-mcp-client)\n* 6. [\ud83d\ude80 Quick Start: VS Code Dev Container](#-quick-start-vs-code-dev-container)\n * 6.1. [1 - Clone & Open](#1---clone--open)\n * 6.2. [2 - First-Time Build (Automatic)](#2---first-time-build-automatic)\n* 7. [Quick Start (manual install)](#quick-start-manual-install)\n * 7.1. [Prerequisites](#prerequisites)\n * 7.2. [One-liner (dev)](#one-liner-dev)\n * 7.3. [Containerised (self-signed TLS)](#containerised-self-signed-tls)\n * 7.4. [Smoke-test the API](#smoke-test-the-api)\n* 8. [Installation](#installation)\n * 8.1. [Via Make](#via-make)\n * 8.2. [UV (alternative)](#uv-alternative)\n * 8.3. [pip (alternative)](#pip-alternative)\n * 8.4. [Optional (PostgreSQL adapter)](#optional-postgresql-adapter)\n * 8.4.1. [Quick Postgres container](#quick-postgres-container)\n* 9. [Configuration (`.env` or env vars)](#configuration-env-or-env-vars)\n * 9.1. [Basic](#basic)\n * 9.2. [Authentication](#authentication)\n * 9.3. [UI Features](#ui-features)\n * 9.4. [Security](#security)\n * 9.5. [Logging](#logging)\n * 9.6. [Transport](#transport)\n * 9.7. [Federation](#federation)\n * 9.8. [Resources](#resources)\n * 9.9. [Tools](#tools)\n * 9.10. [Prompts](#prompts)\n * 9.11. [Health Checks](#health-checks)\n * 9.12. [Database](#database)\n * 9.13. [Cache Backend](#cache-backend)\n * 9.14. [Development](#development)\n* 10. [Running](#running)\n * 10.1. [Makefile](#makefile)\n * 10.2. [Script helper](#script-helper)\n * 10.3. [Manual (Uvicorn)](#manual-uvicorn)\n* 11. [Authentication examples](#authentication-examples)\n* 12. [\u2601\ufe0f AWS / Azure / OpenShift](#\ufe0f-aws--azure--openshift)\n* 13. [\u2601\ufe0f IBM Cloud Code Engine Deployment](#\ufe0f-ibm-cloud-code-engine-deployment)\n * 13.1. [\ud83d\udd27 Prerequisites](#-prerequisites-1)\n * 13.2. [\ud83d\udce6 Environment Variables](#-environment-variables)\n * 13.3. [\ud83d\ude80 Make Targets](#-make-targets)\n * 13.4. [\ud83d\udcdd Example Workflow](#-example-workflow)\n* 14. [API Endpoints](#api-endpoints)\n* 15. [Testing](#testing)\n* 16. [Project Structure](#project-structure)\n* 17. [API Documentation](#api-documentation)\n* 18. [Makefile targets](#makefile-targets)\n* 19. [\ud83d\udd0d Troubleshooting](#-troubleshooting)\n * 19.1. [Diagnose the listener](#diagnose-the-listener)\n * 19.2. [Why localhost fails on Windows](#why-localhost-fails-on-windows)\n * 19.2.1. [Fix (Podman rootless)](#fix-podman-rootless)\n * 19.2.2. [Fix (Docker Desktop > 4.19)](#fix-docker-desktop--419)\n* 20. [Contributing](#contributing)\n* 21. [Changelog](#changelog)\n* 22. [License](#license)\n* 23. [Core Authors and Maintainers](#core-authors-and-maintainers)\n* 24. [Star History and Project Activity](#star-history-and-project-activity)\n\n<!-- vscode-markdown-toc-config\n numbering=true\n autoSave=true\n /vscode-markdown-toc-config -->\n<!-- /vscode-markdown-toc -->\n\n\n\n## \ud83d\ude80 Overview & Goals\n\n**ContextForge MCP Gateway** is a production-grade gateway, registry, and proxy that sits in front of any [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server or REST API-exposing a unified endpoint for all your AI clients.\n\nIt supports:\n\n* Federation across multiple MCP and REST services\n* Virtualization of legacy APIs as MCP-compliant tools and servers\n* Transport over HTTP, JSON-RPC, WebSocket, SSE, stdio and streamable-HTTP\n* An Admin UI for real-time management and configuration\n* Built-in auth, observability, retries, and rate-limiting\n* Scalable deployments via Docker or PyPI, Redis-backed caching, and multi-cluster federation\n\n\n\nFor a list of upcoming features, check out the [ContextForge MCP Gateway Roadmap](https://ibm.github.io/mcp-context-forge/architecture/roadmap/)\n\n---\n\n<details>\n<summary><strong>\ud83d\udd0c Gateway Layer with Protocol Flexibility</strong></summary>\n\n* Sits in front of any MCP server or REST API\n* Lets you choose your MCP protocol version (e.g., `2025-03-26`)\n* Exposes a single, unified interface for diverse backends\n\n</details>\n\n<details>\n<summary><strong>\ud83c\udf10 Federation of Peer Gateways (MCP Registry)</strong></summary>\n\n* Auto-discovers or configures peer gateways (via mDNS or manual)\n* Performs health checks and merges remote registries transparently\n* Supports Redis-backed syncing and fail-over\n\n</details>\n\n<details>\n<summary><strong>\ud83e\udde9 Virtualization of REST/gRPC Services</strong></summary>\n\n* Wraps non-MCP services as virtual MCP servers\n* Registers tools, prompts, and resources with minimal configuration\n\n</details>\n\n<details>\n<summary><strong>\ud83d\udd01 REST-to-MCP Tool Adapter</strong></summary>\n\n* Adapts REST APIs into tools with:\n\n * Automatic JSON Schema extraction\n * Support for headers, tokens, and custom auth\n * Retry, timeout, and rate-limit policies\n\n</details>\n\n<details>\n<summary><strong>\ud83e\udde0 Unified Registries</strong></summary>\n\n* **Prompts**: Jinja2 templates, multimodal support, rollback/versioning\n* **Resources**: URI-based access, MIME detection, caching, SSE updates\n* **Tools**: Native or adapted, with input validation and concurrency controls\n\n</details>\n\n<details>\n<summary><strong>\ud83d\udcc8 Admin UI, Observability & Dev Experience</strong></summary>\n\n* Admin UI built with HTMX + Alpine.js\n* Auth: Basic, JWT, or custom schemes\n* Structured logs, health endpoints, metrics\n* 400+ tests, Makefile targets, live reload, pre-commit hooks\n\n</details>\n\n---\n\n## Quick Start - PyPI\n\nMCP Gateway is published on [PyPI](https://pypi.org/project/mcp-contextforge-gateway/) as `mcp-contextforge-gateway`.\n\n---\n\n<details>\n<summary><strong>\ud83d\udccb Prerequisites</strong></summary>\n\n* **Python \u2265 3.10** (3.11 recommended)\n* **curl + jq** - only for the last smoke-test step\n\n</details>\n\n### 1 - Install & run (copy-paste friendly)\n\n```bash\n# 1\ufe0f\u20e3 Isolated env + install from pypi\nmkdir mcpgateway && cd mcpgateway\npython3 -m venv .venv && source .venv/bin/activate\npip install --upgrade pip\npip install mcp-contextforge-gateway\n\n# 2\ufe0f\u20e3 Launch on all interfaces with custom creds & secret key\n# Enable the Admin API endpoints (true/false) - disabled by default\nexport MCPGATEWAY_UI_ENABLED=true\nexport MCPGATEWAY_ADMIN_API_ENABLED=true\n\nBASIC_AUTH_PASSWORD=pass JWT_SECRET_KEY=my-test-key \\\n mcpgateway --host 0.0.0.0 --port 4444 & # admin/pass\n\n# 3\ufe0f\u20e3 Generate a bearer token & smoke-test the API\nexport MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token \\\n --username admin --exp 10080 --secret my-test-key)\n\ncurl -s -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://127.0.0.1:4444/version | jq\n```\n\n<details>\n<summary><strong>More configuration</strong></summary>\n\nCopy [.env.example](.env.example) to `.env` and tweak any of the settings (or use them as env variables).\n\n</details>\n\n<details>\n<summary><strong>\ud83d\ude80 End-to-end demo (register a local MCP server)</strong></summary>\n\n```bash\n# 1\ufe0f\u20e3 Spin up the sample GO MCP time server using mcpgateway.translate & docker\npython3 -m mcpgateway.translate \\\n --stdio \"docker run --rm -i -p 8888:8080 ghcr.io/ibm/fast-time-server:latest -transport=stdio\" \\\n --port 8003\n\n# Or using the official mcp-server-git using uvx:\npip install uv # to install uvx, if not already installed\npython3 -m mcpgateway.translate --stdio \"uvx mcp-server-git\" --port 9000\n\n# Alternative: running the local binary\n# cd mcp-servers/go/fast-time-server; make build\n# python3 -m mcpgateway.translate --stdio \"./dist/fast-time-server -transport=stdio\" --port 8002\n\n# 2\ufe0f\u20e3 Register it with the gateway\ncurl -s -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\":\"fast_time\",\"url\":\"http://localhost:9000/sse\"}' \\\n http://localhost:4444/gateways\n\n# 3\ufe0f\u20e3 Verify tool catalog\ncurl -s -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/tools | jq\n\n# 4\ufe0f\u20e3 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.\ncurl -s -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\":\"time_server\",\"description\":\"Fast time tools\",\"associatedTools\":[<ID_OF_TOOLS>]}' \\\n http://localhost:4444/servers | jq\n\n# Example curl\ncurl -s -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\"\n -H \"Content-Type: application/json\"\n -d '{\"name\":\"time_server\",\"description\":\"Fast time tools\",\"associatedTools\":[\"6018ca46d32a4ac6b4c054c13a1726a2\"]}' \\\n http://localhost:4444/servers | jq\n\n# 5\ufe0f\u20e3 List servers (should now include the UUID of the newly created virtual server)\ncurl -s -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/servers | jq\n\n# 6\ufe0f\u20e3 Client SSE endpoint. Inspect it interactively with the MCP Inspector CLI (or use any MCP client)\nnpx -y @modelcontextprotocol/inspector\n# Transport Type: SSE, URL: http://localhost:4444/servers/UUID_OF_SERVER_1/sse, Header Name: \"Authorization\", Bearer Token\n```\n\n</details>\n\n<details>\n<summary><strong>\ud83d\udda7 Using the stdio wrapper (mcpgateway-wrapper)</strong></summary>\n\n```bash\nexport MCP_AUTH_TOKEN=$MCPGATEWAY_BEARER_TOKEN\nexport MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/UUID_OF_SERVER_1\npython3 -m mcpgateway.wrapper # Ctrl-C to exit\n```\n\nYou can also run it with `uv` or inside Docker/Podman - see the *Containers* section above.\n\nIn MCP Inspector, define `MCP_AUTH_TOKEN` and `MCP_SERVER_CATALOG_URLS` env variables, and select `python3` as the Command, and `-m mcpgateway.wrapper` as Arguments.\n\n```bash\necho $PWD/.venv/bin/python3 # Using the Python3 full path ensures you have a working venv\nexport MCP_SERVER_CATALOG_URLS='http://localhost:4444/servers/UUID_OF_SERVER_1'\nexport MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}\nnpx -y @modelcontextprotocol/inspector\n```\n\nWhen using a MCP Client such as Claude with stdio:\n\n```json\n{\n \"mcpServers\": {\n \"mcpgateway-wrapper\": {\n \"command\": \"python\",\n \"args\": [\"-m\", \"mcpgateway.wrapper\"],\n \"env\": {\n \"MCP_AUTH_TOKEN\": \"your-token-here\",\n \"MCP_SERVER_CATALOG_URLS\": \"http://localhost:4444/servers/UUID_OF_SERVER_1\",\n \"MCP_TOOL_CALL_TIMEOUT\": \"120\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Quick Start - Containers\n\nUse the official OCI image from GHCR with **Docker** *or* **Podman**.\n\n---\n\n### \ud83d\udc33 Docker\n\n#### 1 - Minimum viable run\n\n```bash\ndocker run -d --name mcpgateway \\\n -p 4444:4444 \\\n -e HOST=0.0.0.0 \\\n -e JWT_SECRET_KEY=my-test-key \\\n -e BASIC_AUTH_USER=admin \\\n -e BASIC_AUTH_PASSWORD=changeme \\\n -e AUTH_REQUIRED=true \\\n -e DATABASE_URL=sqlite:///./mcp.db \\\n ghcr.io/ibm/mcp-context-forge:0.3.1\n\n# Tail logs (Ctrl+C to quit)\ndocker logs -f mcpgateway\n\n# Generating an API key\ndocker run --rm -it ghcr.io/ibm/mcp-context-forge:0.3.1 \\\n python -m mcpgateway.utils.create_jwt_token --username admin --exp 0 --secret my-test-key\n```\n\nBrowse to **[http://localhost:4444/admin](http://localhost:4444/admin)** (user `admin` / pass `changeme`).\n\n#### 2 - Persist the SQLite database\n\n```bash\nmkdir -p $(pwd)/data\n\ntouch $(pwd)/data/mcp.db\n\nsudo chown -R :docker $(pwd)/data\n\nchmod 777 $(pwd)/data\n\ndocker run -d --name mcpgateway \\\n --restart unless-stopped \\\n -p 4444:4444 \\\n -v $(pwd)/data:/data \\\n -e DATABASE_URL=sqlite:////data/mcp.db \\\n -e HOST=0.0.0.0 \\\n -e JWT_SECRET_KEY=my-test-key \\\n -e BASIC_AUTH_USER=admin \\\n -e BASIC_AUTH_PASSWORD=changeme \\\n ghcr.io/ibm/mcp-context-forge:0.3.1\n```\n\nSQLite now lives on the host at `./data/mcp.db`.\n\n#### 3 - Local tool discovery (host network)\n\n```bash\nmkdir -p $(pwd)/data\n\ntouch $(pwd)/data/mcp.db\n\nsudo chown -R :docker $(pwd)/data\n\nchmod 777 $(pwd)/data\n\ndocker run -d --name mcpgateway \\\n --network=host \\\n -e HOST=0.0.0.0 \\\n -e PORT=4444 \\\n -e DATABASE_URL=sqlite:////data/mcp.db \\\n -v $(pwd)/data:/data \\\n ghcr.io/ibm/mcp-context-forge:0.3.1\n```\n\nUsing `--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](https://docs.docker.com/engine/network/drivers/host/) for more details.\n\n---\n\n### \ud83e\uddad Podman (rootless-friendly)\n\n#### 1 - Basic run\n\n```bash\npodman run -d --name mcpgateway \\\n -p 4444:4444 \\\n -e HOST=0.0.0.0 \\\n -e DATABASE_URL=sqlite:///./mcp.db \\\n ghcr.io/ibm/mcp-context-forge:0.3.1\n```\n\n#### 2 - Persist SQLite\n\n```bash\nmkdir -p $(pwd)/data\n\ntouch $(pwd)/data/mcp.db\n\nsudo chown -R :docker $(pwd)/data\n\nchmod 777 $(pwd)/data\n\npodman run -d --name mcpgateway \\\n --restart=on-failure \\\n -p 4444:4444 \\\n -v $(pwd)/data:/data \\\n -e DATABASE_URL=sqlite:////data/mcp.db \\\n ghcr.io/ibm/mcp-context-forge:0.3.1\n```\n\n#### 3 - Host networking (rootless)\n\n```bash\nmkdir -p $(pwd)/data\n\ntouch $(pwd)/data/mcp.db\n\nsudo chown -R :docker $(pwd)/data\n\nchmod 777 $(pwd)/data\n\npodman run -d --name mcpgateway \\\n --network=host \\\n -v $(pwd)/data:/data \\\n -e DATABASE_URL=sqlite:////data/mcp.db \\\n ghcr.io/ibm/mcp-context-forge:0.3.1\n```\n\n---\n\n<details>\n<summary><strong>\u270f\ufe0f Docker/Podman tips</strong></summary>\n\n* **.env files** - Put all the `-e FOO=` lines into a file and replace them with `--env-file .env`. See the provided [.env.example](.env.example) for reference.\n* **Pinned tags** - Use an explicit version (e.g. `v0.3.1`) instead of `latest` for reproducible builds.\n* **JWT tokens** - Generate one in the running container:\n\n ```bash\n docker exec mcpgateway python3 -m mcpgateway.utils.create_jwt_token -u admin -e 10080 --secret my-test-key\n ```\n* **Upgrades** - Stop, remove, and rerun with the same `-v $(pwd)/data:/data` mount; your DB and config stay intact.\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\ude91 Smoke-test the running container</strong></summary>\n\n```bash\ncurl -s -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://localhost:4444/health | jq\ncurl -s -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://localhost:4444/tools | jq\ncurl -s -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://localhost:4444/version | jq\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udda7 Running the MCP Gateway stdio wrapper</strong></summary>\n\nThe `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.\n\n```bash\n# Set environment variables\nexport MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 10080 --secret my-test-key)\nexport MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}\nexport MCP_SERVER_CATALOG_URLS='http://localhost:4444/servers/UUID_OF_SERVER_1'\nexport MCP_TOOL_CALL_TIMEOUT=120\nexport MCP_WRAPPER_LOG_LEVEL=DEBUG # or OFF to disable logging\n\ndocker run --rm -i \\\n -e MCP_AUTH_TOKEN=$MCPGATEWAY_BEARER_TOKEN \\\n -e MCP_SERVER_CATALOG_URLS=http://host.docker.internal:4444/servers/UUID_OF_SERVER_1 \\\n -e MCP_TOOL_CALL_TIMEOUT=120 \\\n -e MCP_WRAPPER_LOG_LEVEL=DEBUG \\\n ghcr.io/ibm/mcp-context-forge:0.3.1 \\\n python3 -m mcpgateway.wrapper\n```\n\n</details>\n\n---\n\n## Testing `mcpgateway.wrapper` by hand:\n\nBecause the wrapper speaks JSON-RPC over stdin/stdout, you can interact with it using nothing more than a terminal or pipes.\n\n```bash\n# Start the MCP Gateway Wrapper\nexport MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}\nexport MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/YOUR_SERVER_UUID\npython3 -m mcpgateway.wrapper\n```\n\n<details>\n<summary><strong>Initialize the protocol</strong></summary>\n\n```json\n# Initialize the protocol\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{},\"clientInfo\":{\"name\":\"demo\",\"version\":\"0.0.1\"}}}\n\n# Then after the reply:\n{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\",\"params\":{}}\n\n# Get prompts\n{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"prompts/list\"}\n{\"jsonrpc\":\"2.0\",\"id\":5,\"method\":\"prompts/get\",\"params\":{\"name\":\"greeting\",\"arguments\":{\"user\":\"Bob\"}}}\n\n# Get resources\n{\"jsonrpc\":\"2.0\",\"id\":6,\"method\":\"resources/list\"}\n{\"jsonrpc\":\"2.0\",\"id\":7,\"method\":\"resources/read\",\"params\":{\"uri\":\"https://example.com/some.txt\"}}\n\n# Get / call tools\n{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\"}\n{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{\"name\":\"get_system_time\",\"arguments\":{\"timezone\":\"Europe/Dublin\"}}}\n```\n\n</details>\n\n<details>\n<summary><strong>Expected responses from mcpgateway.wrapper</strong></summary>\n\n```json\n{\"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.3.0\"}}}\n\n# When there's no tools\n{\"jsonrpc\":\"2.0\",\"id\":2,\"result\":{\"tools\":[]}}\n\n# After you add some tools and create a virtual server\n{\"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\"}]}}\n\n# Running the time tool:\n{\"jsonrpc\":\"2.0\",\"id\":3,\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"2025-07-09T00:09:45+01:00\"}]}}\n```\n\n</details>\n\n### \ud83e\udde9 Running from an MCP Client (`mcpgateway.wrapper`)\n\nThe `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.\n\n> **Remember** to substitute your real Gateway URL (and server ID) for `http://localhost:4444/servers/UUID_OF_SERVER_1`.\n> When inside Docker/Podman, that often becomes `http://host.docker.internal:4444/servers/UUID_OF_SERVER_1` (macOS/Windows) or the gateway container's hostname (Linux).\n\n---\n\n<details>\n<summary><strong>\ud83d\udc33 Docker / Podman</strong></summary>\n\n```bash\ndocker run -i --rm \\\n --network=host \\\n -e MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/UUID_OF_SERVER_1 \\\n -e MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \\\n -e MCP_TOOL_CALL_TIMEOUT=120 \\\n ghcr.io/ibm/mcp-context-forge:0.3.1 \\\n python3 -m mcpgateway.wrapper\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udce6 pipx (one-liner install & run)</strong></summary>\n\n```bash\n# Install gateway package in its own isolated venv\npipx install --include-deps mcp-contextforge-gateway\n\n# Run the stdio wrapper\nMCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \\\nMCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/UUID_OF_SERVER_1 \\\npython3 -m mcpgateway.wrapper\n# Alternatively with uv\nuv run --directory . -m mcpgateway.wrapper\n```\n\n**Claude Desktop JSON** (uses the host Python that pipx injected):\n\n```json\n{\n \"mcpServers\": {\n \"mcpgateway-wrapper\": {\n \"command\": \"python3\",\n \"args\": [\"-m\", \"mcpgateway.wrapper\"],\n \"env\": {\n \"MCP_AUTH_TOKEN\": \"<your-token>\",\n \"MCP_SERVER_CATALOG_URLS\": \"http://localhost:4444/servers/UUID_OF_SERVER_1\",\n \"MCP_TOOL_CALL_TIMEOUT\": \"120\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\u26a1 uv / uvx (light-speed venvs)</strong></summary>\n\n#### 1 - Install <code>uv</code> (<code>uvx</code> is an alias it provides)\n\n```bash\n# (a) official one-liner\ncurl -Ls https://astral.sh/uv/install.sh | sh\n\n# (b) or via pipx\npipx install uv\n```\n\n#### 2 - Create an on-the-spot venv & run the wrapper\n\n```bash\n# Create venv in ~/.venv/mcpgateway (or current dir if you prefer)\nuv venv ~/.venv/mcpgateway\nsource ~/.venv/mcpgateway/bin/activate\n\n# Install the gateway package using uv\nuv pip install mcp-contextforge-gateway\n\n# Launch wrapper\nMCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \\\nMCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/UUID_OF_SERVER_1 \\\nuv run --directory . -m mcpgateway.wrapper # Use this just for testing, as the Client will run the uv command\n```\n\n#### Claude Desktop JSON (runs through **uvx**)\n\n```json\n{\n \"mcpServers\": {\n \"mcpgateway-wrapper\": {\n \"command\": \"uvx\",\n \"args\": [\n \"run\",\n \"--\",\n \"python\",\n \"-m\",\n \"mcpgateway.wrapper\"\n ],\n \"env\": {\n \"MCP_AUTH_TOKEN\": \"<your-token>\",\n \"MCP_SERVER_CATALOG_URLS\": \"http://localhost:4444/servers/UUID_OF_SERVER_1\"\n }\n }\n}\n```\n\n</details>\n\n---\n\n### \ud83d\ude80 Using with Claude Desktop (or any GUI MCP client)\n\n1. **Edit Config** \u2192 `File \u25b8 Settings \u25b8 Developer \u25b8 Edit Config`\n2. Paste one of the JSON blocks above (Docker / pipx / uvx).\n3. Restart the app so the new stdio server is spawned.\n4. Open logs in the same menu to verify `mcpgateway-wrapper` started and listed your tools.\n\nNeed help? See:\n\n* **MCP Debugging Guide** - [https://modelcontextprotocol.io/docs/tools/debugging](https://modelcontextprotocol.io/docs/tools/debugging)\n\n---\n\n## \ud83d\ude80 Quick Start: VS Code Dev Container\n\nSpin up a fully-loaded dev environment (Python 3.11, Docker/Podman CLI, all project dependencies) in just two clicks.\n\n---\n\n<details>\n<summary><strong>\ud83d\udccb Prerequisites</strong></summary>\n\n* **VS Code** with the [Dev Containers extension](https://code.visualstudio.com/docs/devcontainers/containers)\n* **Docker** or **Podman** installed and running locally\n\n</details>\n\n<details>\n<summary><strong>\ud83e\uddf0 Setup Instructions</strong></summary>\n\n### 1 - Clone & Open\n\n```bash\ngit clone https://github.com/ibm/mcp-context-forge.git\ncd mcp-context-forge\ncode .\n```\n\nVS Code will detect the `.devcontainer` and prompt:\n**\"Reopen in Container\"**\n*or* manually run: <kbd>Ctrl/Cmd \u21e7 P</kbd> \u2192 **Dev Containers: Reopen in Container**\n\n---\n\n### 2 - First-Time Build (Automatic)\n\nThe container build will:\n\n* Install system packages & Python 3.11\n* Run `make install-dev` to pull all dependencies\n* Execute tests to verify the toolchain\n\nYou'll land in `/workspace` ready to develop.\n\n</details>\n\n<details>\n<summary><strong>\ud83d\udee0\ufe0f Daily Developer Workflow</strong></summary>\n\nCommon tasks inside the container:\n\n```bash\n# Start dev server (hot reload)\nmake dev # http://localhost:4444\n\n# Run tests & linters\nmake test\nmake lint\n```\n\nOptional:\n\n* `make bash` - drop into an interactive shell\n* `make clean` - clear build artefacts & caches\n* Port forwarding is automatic (customize via `.devcontainer/devcontainer.json`)\n\n</details>\n\n<details>\n<summary><strong>\u2601\ufe0f GitHub Codespaces: 1-Click Cloud IDE</strong></summary>\n\nNo local Docker? Use Codespaces:\n\n1. Go to the repo \u2192 **Code \u25b8 Codespaces \u25b8 Create codespace on main**\n2. Wait for the container image to build in the cloud\n3. Develop using the same workflow above\n\n</details>\n\n---\n\n## Quick Start (manual install)\n\n### Prerequisites\n\n* **Python \u2265 3.10**\n* **GNU Make** (optional, but all common workflows are available as Make targets)\n* Optional: **Docker / Podman** for containerised runs\n\n### One-liner (dev)\n\n```bash\nmake venv install serve\n```\n\nWhat it does:\n\n1. Creates / activates a `.venv` in your home folder `~/.venv/mcpgateway`\n2. Installs the gateway and necessary dependencies\n3. Launches **Gunicorn** (Uvicorn workers) on [http://localhost:4444](http://localhost:4444)\n\nFor development, you can use:\n\n```bash\nmake install-dev # Install development dependencies, ex: linters and test harness\nmake lint # optional: run style checks (ruff, mypy, etc.)\n```\n\n### Containerised (self-signed TLS)\n\n\n> You can use docker or podman, ex:\n\n```bash\nmake podman # build production image\nmake podman-run-ssl # run at https://localhost:4444\n# or listen on port 4444 on your host directly, adds --network=host to podman\nmake podman-run-ssl-host\n```\n\n### Smoke-test the API\n\n```bash\ncurl -k -sX GET \\\n -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n https://localhost:4444/tools | jq\n```\n\nYou should receive `[]` until you register a tool.\n\n---\n\n## Installation\n\n### Via Make\n\n```bash\nmake venv install # create .venv + install deps\nmake serve # gunicorn on :4444\n```\n\n### UV (alternative)\n\n```bash\nuv venv && source .venv/bin/activate\nuv pip install -e '.[dev]' # IMPORTANT: in zsh, quote to disable glob expansion!\n```\n\n### pip (alternative)\n\n```bash\npython3 -m venv .venv && source .venv/bin/activate\npip install -e \".[dev]\"\n```\n\n### Optional (PostgreSQL adapter)\n\nYou can configure the gateway with SQLite, PostgreSQL (or any other compatible database) in .env.\n\nWhen using PostgreSQL, you need to install `psycopg2` driver.\n\n```bash\nuv pip install psycopg2-binary # dev convenience\n# or\nuv pip install psycopg2 # production build\n```\n\n#### Quick Postgres container\n\n```bash\ndocker run --name mcp-postgres \\\n -e POSTGRES_USER=postgres \\\n -e POSTGRES_PASSWORD=mysecretpassword \\\n -e POSTGRES_DB=mcp \\\n -p 5432:5432 -d postgres\n```\n\nA `make compose-up` target is provided along with a [docker-compose.yml](docker-compose.yml) file to make this process simpler.\n\n---\n\n## Configuration (`.env` or env vars)\n\n> \u26a0\ufe0f If any required `.env` variable is missing or invalid, the gateway will fail fast at startup with a validation error via Pydantic.\n\nYou can get started by copying the provided [.env.example](.env.example) to `.env` and making the necessary edits to fit your environment.\n\n<details>\n<summary><strong>\ud83d\udd27 Environment Configuration Variables</strong></summary>\n\n### Basic\n\n| Setting | Description | Default | Options |\n| --------------- | ---------------------------------------- | ---------------------- | ---------------------- |\n| `APP_NAME` | Gateway / OpenAPI title | `MCP Gateway` | string |\n| `HOST` | Bind address for the app | `0.0.0.0` | IPv4/IPv6 |\n| `PORT` | Port the server listens on | `4444` | 1-65535 |\n| `DATABASE_URL` | SQLAlchemy connection URL | `sqlite:///./mcp.db` | any SQLAlchemy dialect |\n| `APP_ROOT_PATH` | Subpath prefix for app (e.g. `/gateway`) | (empty) | string |\n| `TEMPLATES_DIR` | Path to Jinja2 templates | `mcpgateway/templates` | path |\n| `STATIC_DIR` | Path to static files | `mcpgateway/static` | path |\n\n> \ud83d\udca1 Use `APP_ROOT_PATH=/foo` if reverse-proxying under a subpath like `https://host.com/foo/`.\n\n### Authentication\n\n| Setting | Description | Default | Options |\n| --------------------- | ---------------------------------------------------------------- | ------------- | ---------- |\n| `BASIC_AUTH_USER` | Username for Admin UI login and HTTP Basic authentication | `admin` | string |\n| `BASIC_AUTH_PASSWORD` | Password for Admin UI login and HTTP Basic authentication | `changeme` | string |\n| `AUTH_REQUIRED` | Require authentication for all API routes | `true` | bool |\n| `JWT_SECRET_KEY` | Secret key used to **sign JWT tokens** for API access | `my-test-key` | string |\n| `JWT_ALGORITHM` | Algorithm used to sign the JWTs (`HS256` is default, HMAC-based) | `HS256` | PyJWT algs |\n| `TOKEN_EXPIRY` | Expiry of generated JWTs in minutes | `10080` | int > 0 |\n| `AUTH_ENCRYPTION_SECRET` | Passphrase used to derive AES key for encrypting tool auth headers | `my-test-salt` | string |\n\n> \ud83d\udd10 `BASIC_AUTH_USER`/`PASSWORD` are used for:\n>\n> * Logging into the web-based Admin UI\n> * Accessing APIs via Basic Auth (`curl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\"`)\n>\n> \ud83d\udd11 `JWT_SECRET_KEY` is used to:\n>\n> * Sign JSON Web Tokens (`Authorization: Bearer <token>`)\n> * Generate tokens via:\n>\n> ```bash\n> export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 0 --secret my-test-key)\n> echo $MCPGATEWAY_BEARER_TOKEN\n> ```\n> * Tokens allow non-interactive API clients to authenticate securely.\n>\n> \ud83e\uddea 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.\n> In production, you should use the SSE to stdio `mcpgateway-wrapper` for such tools that don't support authenticated SSE, while still ensuring the gateway uses authentication.\n>\n> \ud83d\udd10 `AUTH_ENCRYPTION_SECRET` is used to encrypt and decrypt tool authentication credentials (`auth_value`).\n> You must set the same value across environments to decode previously stored encrypted auth values.\n> Recommended: use a long, random string.\n\n### UI Features\n\n| Setting | Description | Default | Options |\n| ------------------------------ | -------------------------------------- | ------- | ------- |\n| `MCPGATEWAY_UI_ENABLED` | Enable the interactive Admin dashboard | `true` | bool |\n| `MCPGATEWAY_ADMIN_API_ENABLED` | Enable API endpoints for admin ops | `true` | bool |\n\n> \ud83d\udda5\ufe0f Set both to `false` to disable management UI and APIs in production.\n\n### Security\n\n| Setting | Description | Default | Options |\n| ----------------- | ------------------------------ | ---------------------------------------------- | ---------- |\n| `SKIP_SSL_VERIFY` | Skip upstream TLS verification | `false` | bool |\n| `ALLOWED_ORIGINS` | CORS allow-list | `[\"http://localhost\",\"http://localhost:4444\"]` | JSON array |\n| `CORS_ENABLED` | Enable CORS | `true` | bool |\n\n> Note: do not quote the ALLOWED_ORIGINS values, this needs to be valid JSON, such as: `ALLOWED_ORIGINS=[\"http://localhost\", \"http://localhost:4444\"]`\n\n### Logging\n\n| Setting | Description | Default | Options |\n| ------------ | ----------------- | ------- | ------------------ |\n| `LOG_LEVEL` | Minimum log level | `INFO` | `DEBUG`...`CRITICAL` |\n| `LOG_FORMAT` | Log format | `json` | `json`, `text` |\n| `LOG_FILE` | Log output file | (none) | path or empty |\n\n### Transport\n\n| Setting | Description | Default | Options |\n| ------------------------- | ---------------------------------- | ------- | ------------------------------- |\n| `TRANSPORT_TYPE` | Enabled transports | `all` | `http`,`ws`,`sse`,`stdio`,`all` |\n| `WEBSOCKET_PING_INTERVAL` | WebSocket ping (secs) | `30` | int > 0 |\n| `SSE_RETRY_TIMEOUT` | SSE retry timeout (ms) | `5000` | int > 0 |\n| `USE_STATEFUL_SESSIONS` | streamable http config | `false` | bool |\n| `JSON_RESPONSE_ENABLED` | json/sse streams (streamable http) | `true` | bool |\n\n### Federation\n\n| Setting | Description | Default | Options |\n| -------------------------- | ---------------------- | ------- | ---------- |\n| `FEDERATION_ENABLED` | Enable federation | `true` | bool |\n| `FEDERATION_DISCOVERY` | Auto-discover peers | `false` | bool |\n| `FEDERATION_PEERS` | Comma-sep peer URLs | `[]` | JSON array |\n| `FEDERATION_TIMEOUT` | Gateway timeout (secs) | `30` | int > 0 |\n| `FEDERATION_SYNC_INTERVAL` | Sync interval (secs) | `300` | int > 0 |\n\n### Resources\n\n| Setting | Description | Default | Options |\n| --------------------- | --------------------- | ---------- | ---------- |\n| `RESOURCE_CACHE_SIZE` | LRU cache size | `1000` | int > 0 |\n| `RESOURCE_CACHE_TTL` | Cache TTL (seconds) | `3600` | int > 0 |\n| `MAX_RESOURCE_SIZE` | Max resource bytes | `10485760` | int > 0 |\n| `ALLOWED_MIME_TYPES` | Acceptable MIME types | see code | JSON array |\n\n### Tools\n\n| Setting | Description | Default | Options |\n| ----------------------- | ------------------------------ | ------- | ------- |\n| `TOOL_TIMEOUT` | Tool invocation timeout (secs) | `60` | int > 0 |\n| `MAX_TOOL_RETRIES` | Max retry attempts | `3` | int \u2265 0 |\n| `TOOL_RATE_LIMIT` | Tool calls per minute | `100` | int > 0 |\n| `TOOL_CONCURRENT_LIMIT` | Concurrent tool invocations | `10` | int > 0 |\n\n### Prompts\n\n| Setting | Description | Default | Options |\n| ----------------------- | -------------------------------- | -------- | ------- |\n| `PROMPT_CACHE_SIZE` | Cached prompt templates | `100` | int > 0 |\n| `MAX_PROMPT_SIZE` | Max prompt template size (bytes) | `102400` | int > 0 |\n| `PROMPT_RENDER_TIMEOUT` | Jinja render timeout (secs) | `10` | int > 0 |\n\n### Health Checks\n\n| Setting | Description | Default | Options |\n| ----------------------- | ----------------------------------------- | ------- | ------- |\n| `HEALTH_CHECK_INTERVAL` | Health poll interval (secs) | `60` | int > 0 |\n| `HEALTH_CHECK_TIMEOUT` | Health request timeout (secs) | `10` | int > 0 |\n| `UNHEALTHY_THRESHOLD` | Fail-count before peer deactivation, | `3` | int > 0 |\n| | Set to -1 if deactivation is not needed. | | |\n\n### Database\n\n| Setting | Description | Default | Options |\n| ----------------------- | ------------------------------- | ------- | ------- |\n| `DB_POOL_SIZE` . | SQLAlchemy connection pool size | `200` | int > 0 |\n| `DB_MAX_OVERFLOW`. | Extra connections beyond pool | `10` | int \u2265 0 |\n| `DB_POOL_TIMEOUT`. | Wait for connection (secs) | `30` | int > 0 |\n| `DB_POOL_RECYCLE`. | Recycle connections (secs) | `3600` | int > 0 |\n| `DB_MAX_RETRIES` . | Max Retry Attempts | `3` | int > 0 |\n| `DB_RETRY_INTERVAL_MS` | Retry Interval (ms) | `2000` | int > 0 |\n\n### Cache Backend\n\n| Setting | Description | Default | Options |\n| ------------------------- | -------------------------- | -------- | ------------------------ |\n| `CACHE_TYPE` | Backend (`memory`/`redis`) | `memory` | `none`, `memory`,`redis` |\n| `REDIS_URL` | Redis connection URL | (none) | string or empty |\n| `CACHE_PREFIX` | Key prefix | `mcpgw:` | string |\n| `REDIS_MAX_RETRIES` | Max Retry Attempts | `3` | int > 0 |\n| `REDIS_RETRY_INTERVAL_MS` | Retry Interval (ms) | `2000` | int > 0 |\n\n> \ud83e\udde0 `none` disables caching entirely. Use `memory` for dev, `database` for persistence, or `redis` for distributed caching.\n\n### Development\n\n| Setting | Description | Default | Options |\n| ---------- | ---------------------- | ------- | ------- |\n| `DEV_MODE` | Enable dev mode | `false` | bool |\n| `RELOAD` | Auto-reload on changes | `false` | bool |\n| `DEBUG` | Debug logging | `false` | bool |\n\n</details>\n\n---\n\n## Running\n\n### Makefile\n\n```bash\n make serve # Run production Gunicorn server on\n make serve-ssl # Run Gunicorn behind HTTPS on :4444 (uses ./certs)\n```\n\n### Script helper\n\nTo run the development (uvicorn) server:\n\n```bash\nmake dev\n# or\n./run.sh --reload --log debug --workers 2\n```\n\n> `run.sh` is a wrapper around `uvicorn` that loads `.env`, supports reload, and passes arguments to the server.\n\nKey flags:\n\n| Flag | Purpose | Example |\n| ---------------- | ---------------- | ------------------ |\n| `-e, --env FILE` | load env-file | `--env prod.env` |\n| `-H, --host` | bind address | `--host 127.0.0.1` |\n| `-p, --port` | listen port | `--port 8080` |\n| `-w, --workers` | gunicorn workers | `--workers 4` |\n| `-r, --reload` | auto-reload | `--reload` |\n\n### Manual (Uvicorn)\n\n```bash\nuvicorn mcpgateway.main:app --host 0.0.0.0 --port 4444 --workers 4\n```\n\n---\n\n## Authentication examples\n\n```bash\n# Generate a JWT token using JWT_SECRET_KEY and export it as MCPGATEWAY_BEARER_TOKEN\n# Note that the module needs to be installed. If running locally use:\nexport MCPGATEWAY_BEARER_TOKEN=$(JWT_SECRET_KEY=my-test-key python3 -m mcpgateway.utils.create_jwt_token)\n\n# Use the JWT token in an API call\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/tools\n```\n\n---\n\n## \u2601\ufe0f AWS / Azure / OpenShift\n\nDeployment details can be found in the GitHub Pages.\n\n## \u2601\ufe0f IBM Cloud Code Engine Deployment\n\nThis project supports deployment to [IBM Cloud Code Engine](https://cloud.ibm.com/codeengine) using the **ibmcloud** CLI and the IBM Container Registry.\n\n<details>\n<summary><strong>\u2601\ufe0f IBM Cloud Code Engine Deployment</strong></summary>\n\n### \ud83d\udd27 Prerequisites\n\n- Podman **or** Docker installed locally\n- IBM Cloud CLI (use `make ibmcloud-cli-install` to install)\n- An [IBM Cloud API key](https://cloud.ibm.com/iam/apikeys) with access to Code Engine & Container Registry\n- Code Engine and Container Registry services **enabled** in your IBM Cloud account\n\n---\n\n### \ud83d\udce6 Environment Variables\n\nCreate a **`.env`** file (or export the variables in your shell).\nThe first block is **required**; the second provides **tunable defaults** you can override:\n\n```bash\n# \u2500\u2500 Required \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nIBMCLOUD_REGION=us-south\nIBMCLOUD_RESOURCE_GROUP=default\nIBMCLOUD_PROJECT=my-codeengine-project\nIBMCLOUD_CODE_ENGINE_APP=mcpgateway\nIBMCLOUD_IMAGE_NAME=us.icr.io/myspace/mcpgateway:latest\nIBMCLOUD_IMG_PROD=mcpgateway/mcpgateway\nIBMCLOUD_API_KEY=your_api_key_here # Optional - omit to use interactive `ibmcloud login --sso`\n\n# \u2500\u2500 Optional overrides (sensible defaults provided) \u2500\u2500\u2500\u2500\u2500\u2500\nIBMCLOUD_CPU=1 # vCPUs for the app\nIBMCLOUD_MEMORY=4G # Memory allocation\nIBMCLOUD_REGISTRY_SECRET=my-regcred # Name of the Container Registry secret\n```\n\n> \u2705 **Quick check:** `make ibmcloud-check-env`\n\n---\n\n### \ud83d\ude80 Make Targets\n\n| Target | Purpose |\n| --------------------------- | ------------------------------------------------------------------------- |\n| `make ibmcloud-cli-install` | Install IBM Cloud CLI and required plugins |\n| `make ibmcloud-login` | Log in to IBM Cloud (API key or SSO) |\n| `make ibmcloud-ce-login` | Select the Code Engine project & region |\n| `make ibmcloud-tag` | Tag the local container image |\n| `make ibmcloud-push` | Push the image to IBM Container Registry |\n| `make ibmcloud-deploy` | **Create or update** the Code Engine application (uses CPU/memory/secret) |\n| `make ibmcloud-ce-status` | Show current deployment status |\n| `make ibmcloud-ce-logs` | Stream logs from the running app |\n| `make ibmcloud-ce-rm` | Delete the Code Engine application |\n\n---\n\n### \ud83d\udcdd Example Workflow\n\n```bash\nmake ibmcloud-check-env\nmake ibmcloud-cli-install\nmake ibmcloud-login\nmake ibmcloud-ce-login\nmake ibmcloud-tag\nmake ibmcloud-push\nmake ibmcloud-deploy\nmake ibmcloud-ce-status\nmake ibmcloud-ce-logs\n```\n\n</details>\n\n---\n\n## API Endpoints\n\nYou can test the API endpoints through curl, or Swagger UI, and check detailed documentation on ReDoc:\n\n* **Swagger UI** \u2192 [http://localhost:4444/docs](http://localhost:4444/docs)\n* **ReDoc** \u2192 [http://localhost:4444/redoc](http://localhost:4444/redoc)\n\nGenerate an API Bearer token, and test the various API endpoints.\n\n<details>\n<summary><strong>\ud83d\udd10 Authentication & Health Checks</strong></summary>\n\n```bash\n# Generate a bearer token using the configured secret key (use the same as your .env)\nexport MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token -u admin --secret my-test-key)\necho ${MCPGATEWAY_BEARER_TOKEN}\n\n# Quickly confirm that authentication works and the gateway is healthy\ncurl -s -k -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" https://localhost:4444/health\n# {\"status\":\"healthy\"}\n\n# Quickly confirm the gateway version & DB connectivity\ncurl -s -k -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" https://localhost:4444/version | jq\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83e\uddf1 Protocol APIs (MCP) /protocol</strong></summary>\n\n```bash\n# Initialize MCP session\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"protocol_version\":\"2025-03-26\",\n \"capabilities\":{},\n \"client_info\":{\"name\":\"MyClient\",\"version\":\"1.0.0\"}\n }' \\\n http://localhost:4444/protocol/initialize\n\n# Ping (JSON-RPC style)\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"ping\"}' \\\n http://localhost:4444/protocol/ping\n\n# Completion for prompt/resource arguments (not implemented)\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"ref\":{\"type\":\"ref/prompt\",\"name\":\"example_prompt\"},\n \"argument\":{\"name\":\"topic\",\"value\":\"py\"}\n }' \\\n http://localhost:4444/protocol/completion/complete\n\n# Sampling (streaming) (not implemented)\ncurl -N -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"messages\":[{\"role\":\"user\",\"content\":{\"type\":\"text\",\"text\":\"Hello\"}}],\n \"maxTokens\":16\n }' \\\n http://localhost:4444/protocol/sampling/createMessage\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83e\udde0 JSON-RPC Utility /rpc</strong></summary>\n\n```bash\n# Generic JSON-RPC calls (tools, gateways, roots, etc.)\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"list_tools\"}' \\\n http://localhost:4444/rpc\n```\n\nHandles any method name: `list_tools`, `list_gateways`, `prompts/get`, or invokes a tool if method matches a registered tool name .\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udd27 Tool Management /tools</strong></summary>\n\n\n```bash\n# Register a new tool\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\":\"clock_tool\",\n \"url\":\"http://localhost:9000/rpc\",\n \"description\":\"Returns current time\",\n \"input_schema\":{\n \"type\":\"object\",\n \"properties\":{\"timezone\":{\"type\":\"string\"}},\n \"required\":[]\n }\n }' \\\n http://localhost:4444/tools\n\n# List tools\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/tools\n\n# Get tool by ID\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/tools/1\n\n# Update tool\ncurl -X PUT -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{ \"description\":\"Updated desc\" }' \\\n http://localhost:4444/tools/1\n\n# Toggle active status\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://localhost:4444/tools/1/toggle?activate=false\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://localhost:4444/tools/1/toggle?activate=true\n\n# Delete tool\ncurl -X DELETE -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/tools/1\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83c\udf10 Gateway Management /gateways</strong></summary>\n\n```bash\n# Register an MCP server as a new gateway provider\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\":\"peer_gateway\",\"url\":\"http://peer:4444\"}' \\\n http://localhost:4444/gateways\n\n# List gateways\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/gateways\n\n# Get gateway by ID\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/gateways/1\n\n# Update gateway\ncurl -X PUT -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"description\":\"New description\"}' \\\n http://localhost:4444/gateways/1\n\n# Toggle active status\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://localhost:4444/gateways/1/toggle?activate=false\n\n# Delete gateway\ncurl -X DELETE -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/gateways/1\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udcc1 Resource Management /resources</strong></summary>\n\n\n```bash\n# Register resource\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"uri\":\"config://app/settings\",\n \"name\":\"App Settings\",\n \"content\":\"key=value\"\n }' \\\n http://localhost:4444/resources\n\n# List resources\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/resources\n\n# Read a resource\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/resources/config://app/settings\n\n# Update resource\ncurl -X PUT -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"content\":\"new=value\"}' \\\n http://localhost:4444/resources/config://app/settings\n\n# Delete resource\ncurl -X DELETE -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/resources/config://app/settings\n\n# Subscribe to updates (SSE)\ncurl -N -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/resources/subscribe/config://app/settings\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udcdd Prompt Management /prompts</strong></summary>\n\n```bash\n# Create prompt template\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\":\"greet\",\n \"template\":\"Hello, {{ user }}!\",\n \"argument_schema\":{\n \"type\":\"object\",\n \"properties\":{\"user\":{\"type\":\"string\"}},\n \"required\":[\"user\"]\n }\n }' \\\n http://localhost:4444/prompts\n\n# List prompts\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/prompts\n\n# Get prompt (with args)\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"user\":\"Alice\"}' \\\n http://localhost:4444/prompts/greet\n\n# Get prompt (no args)\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/prompts/greet\n\n# Update prompt\ncurl -X PUT -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"template\":\"Hi, {{ user }}!\"}' \\\n http://localhost:4444/prompts/greet\n\n# Toggle active\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://localhost:4444/prompts/5/toggle?activate=false\n\n# Delete prompt\ncurl -X DELETE -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/prompts/greet\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83c\udf32 Root Management /roots</strong></summary>\n\n```bash\n# List roots\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/roots\n\n# Add root\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"uri\":\"/data\",\"name\":\"Data Root\"}' \\\n http://localhost:4444/roots\n\n# Remove root\ncurl -X DELETE -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/roots/%2Fdata\n\n# Subscribe to root changes (SSE)\ncurl -N -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/roots/changes\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udda5\ufe0f Server Management /servers</strong></summary>\n\n```bash\n# List servers\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/servers\n\n# Get server\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/servers/UUID_OF_SERVER_1\n\n# Create server\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\":\"db\",\"description\":\"Database\",\"associatedTools\": [\"1\",\"2\",\"3\"]}' \\\n http://localhost:4444/servers\n\n# Update server\ncurl -X PUT -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"description\":\"Updated\"}' \\\n http://localhost:4444/servers/UUID_OF_SERVER_1\n\n# Toggle active\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n http://localhost:4444/servers/UUID_OF_SERVER_1/toggle?activate=false\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udcca Metrics /metrics</strong></summary>\n\n```bash\n# Get aggregated metrics\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/metrics\n\n# Reset metrics (all or per-entity)\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/metrics/reset\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/metrics/reset?entity=tool&id=1\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udce1 Events & Health</strong></summary>\n\n```bash\n# SSE: all events\ncurl -N -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/events\n\n# WebSocket\nwscat -c ws://localhost:4444/ws \\\n -H \"Authorization: Basic $(echo -n admin:changeme|base64)\"\n\n# Health check\ncurl http://localhost:4444/health\n```\n\nFull Swagger UI at `/docs`.\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udee0\ufe0f Sample Tool</strong></summary>\n\n```bash\nuvicorn sample_tool.clock_tool:app --host 0.0.0.0 --port 9000\n```\n\n```bash\ncurl -X POST -H \"Content-Type: application/json\" \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"get_time\",\"params\":{\"timezone\":\"UTC\"}}' \\\n http://localhost:9000/rpc\n```\n\n</details>\n\n---\n\n## Testing\n\n```bash\nmake test # Run unit tests\nmake lint # Run lint tools\n```\n\n---\n\n## Project Structure\n\n<details>\n<summary><strong>\ud83d\udcc1 Directory and file structure for mcpgateway</strong></summary>\n\n```bash\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 CI / Quality & Meta-files \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\u251c\u2500\u2500 .bumpversion.cfg # Automated semantic-version bumps\n\u251c\u2500\u2500 .coveragerc # Coverage.py settings\n\u251c\u2500\u2500 .darglint # Doc-string linter rules\n\u251c\u2500\u2500 .dockerignore # Context exclusions for image builds\n\u251c\u2500\u2500 .editorconfig # Consistent IDE / editor behaviour\n\u251c\u2500\u2500 .env # Local runtime variables (git-ignored)\n\u251c\u2500\u2500 .env.ce # IBM Code Engine runtime env (ignored)\n\u251c\u2500\u2500 .env.ce.example # Sample env for IBM Code Engine\n\u251c\u2500\u2500 .env.example # Generic sample env file\n\u251c\u2500\u2500 .env.gcr # Google Cloud Run runtime env (ignored)\n\u251c\u2500\u2500 .eslintrc.json # ESLint rules for JS / TS assets\n\u251c\u2500\u2500 .flake8 # Flake-8 configuration\n\u251c\u2500\u2500 .gitattributes # Git attributes (e.g. EOL normalisation)\n\u251c\u2500\u2500 .github # GitHub settings, CI/CD workflows & templates\n\u2502 \u251c\u2500\u2500 CODEOWNERS # Default reviewers\n\u2502 \u2514\u2500\u2500 workflows/ # Bandit, Docker, CodeQL, Python Package, Container Deployment, etc.\n\u251c\u2500\u2500 .gitignore # Git exclusion rules\n\u251c\u2500\u2500 .hadolint.yaml # Hadolint rules for Dockerfiles\n\u251c\u2500\u2500 .htmlhintrc # HTMLHint rules\n\u251c\u2500\u2500 .markdownlint.json # Markdown-lint rules\n\u251c\u2500\u2500 .pre-commit-config.yaml # Pre-commit hooks (ruff, black, mypy, ...)\n\u251c\u2500\u2500 .pycodestyle # PEP-8 checker settings\n\u251c\u2500\u2500 .pylintrc # Pylint configuration\n\u251c\u2500\u2500 .pyspelling.yml # Spell-checker dictionary & filters\n\u251c\u2500\u2500 .ruff.toml # Ruff linter / formatter settings\n\u251c\u2500\u2500 .spellcheck-en.txt # Extra dictionary entries\n\u251c\u2500\u2500 .stylelintrc.json # Stylelint rules for CSS\n\u251c\u2500\u2500 .travis.yml # Legacy Travis CI config (reference)\n\u251c\u2500\u2500 .whitesource # WhiteSource security-scanning config\n\u251c\u2500\u2500 .yamllint # yamllint ruleset\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Documentation & Guidance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\u251c\u2500\u2500 CHANGELOG.md # Version-by-version change log\n\u251c\u2500\u2500 CODE_OF_CONDUCT.md # Community behaviour guidelines\n\u251c\u2500\u2500 CONTRIBUTING.md # How to file issues & send PRs\n\u251c\u2500\u2500 DEVELOPING.md # Contributor workflows & style guide\n\u251c\u2500\u2500 LICENSE # Apache License 2.0\n\u251c\u2500\u2500 README.md # Project overview & quick-start\n\u251c\u2500\u2500 SECURITY.md # Security policy & CVE disclosure process\n\u251c\u2500\u2500 TESTING.md # Testing strategy, fixtures & guidelines\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Containerisation & Runtime \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\u251c\u2500\u2500 Containerfile # OCI image build (Docker / Podman)\n\u251c\u2500\u2500 Containerfile.lite # FROM scratch UBI-Micro production build\n\u251c\u2500\u2500 docker-compose.yml # Local multi-service stack\n\u251c\u2500\u2500 podman-compose-sonarqube.yaml # One-liner SonarQube stack\n\u251c\u2500\u2500 run-gunicorn.sh # Opinionated Gunicorn startup script\n\u251c\u2500\u2500 run.sh # Uvicorn shortcut with arg parsing\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Build / Packaging / Tooling \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\u251c\u2500\u2500 MANIFEST.in # sdist inclusion rules\n\u251c\u2500\u2500 Makefile # Dev & deployment targets\n\u251c\u2500\u2500 package-lock.json # Deterministic npm lock-file\n\u251c\u2500\u2500 package.json # Front-end / docs tooling deps\n\u251c\u2500\u2500 pyproject.toml # Poetry / PDM config & lint rules\n\u251c\u2500\u2500 sonar-code.properties # SonarQube analysis settings\n\u251c\u2500\u2500 uv.lock # UV resolver lock-file\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Kubernetes & Helm Assets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\u251c\u2500\u2500 charts # Helm chart(s) for K8s / OpenShift\n\u2502 \u251c\u2500\u2500 mcp-stack # Umbrella chart\n\u2502 \u2502 \u251c\u2500\u2500 Chart.yaml # Chart metadata\n\u2502 \u2502 \u251c\u2500\u2500 templates/... # Manifest templates\n\u2502 \u2502 \u2514\u2500\u2500 values.yaml # Default values\n\u2502 \u2514\u2500\u2500 README.md # Install / upgrade guide\n\u251c\u2500\u2500 k8s # Raw (non-Helm) K8s manifests\n\u2502 \u2514\u2500\u2500 *.yaml # Deployment, Service, PVC resources\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Documentation Source \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\u251c\u2500\u2500 docs # MkDocs site source\n\u2502 \u251c\u2500\u2500 base.yml # MkDocs \"base\" configuration snippet (do not modify)\n\u2502 \u251c\u2500\u2500 mkdocs.yml # Site configuration (requires base.yml)\n\u2502 \u251c\u2500\u2500 requirements.txt # Python dependencies for the MkDocs site\n\u2502 \u251c\u2500\u2500 Makefile # Make targets for building/serving the docs\n\u2502 \u2514\u2500\u2500 theme # Custom MkDocs theme assets\n\u2502 \u2514\u2500\u2500 logo.png # Logo for the documentation theme\n\u2502 \u2514\u2500\u2500 docs # Markdown documentation\n\u2502 \u251c\u2500\u2500 architecture/ # ADRs for the project\n\u2502 \u251c\u2500\u2500 articles/ # Long-form writeups\n\u2502 \u251c\u2500\u2500 blog/ # Blog posts\n\u2502 \u251c\u2500\u2500 deployment/ # Deployment guides (AWS, Azure, etc.)\n\u2502 \u251c\u2500\u2500 development/ # Development workflows & CI docs\n\u2502 \u251c\u2500\u2500 images/ # Diagrams & screenshots\n\u2502 \u251c\u2500\u2500 index.md # Top-level docs landing page\n\u2502 \u251c\u2500\u2500 manage/ # Management topics (backup, logging, tuning, upgrade)\n\u2502 \u251c\u2500\u2500 overview/ # Feature overviews & UI documentation\n\u2502 \u251c\u2500\u2500 security/ # Security guidance & policies\n\u2502 \u251c\u2500\u2500 testing/ # Testing strategy & fixtures\n\u2502 \u2514\u2500\u2500 using/ # User-facing usage guides (agents, clients, etc.)\n\u2502 \u251c\u2500\u2500 media/ # Social media, press coverage, videos & testimonials\n\u2502 \u2502 \u251c\u2500\u2500 press/ # Press articles and blog posts\n\u2502 \u2502 \u251c\u2500\u2500 social/ # Tweets, LinkedIn posts, YouTube embeds\n\u2502 \u2502 \u251c\u2500\u2500 testimonials/ # Customer quotes & community feedback\n\u2502 \u2502 \u2514\u2500\u2500 kit/ # Media kit & logos for bloggers & press\n\u251c\u2500\u2500 dictionary.dic # Custom dictionary for spell-checker (make spellcheck)\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Application & Libraries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\u251c\u2500\u2500 agent_runtimes # Configurable agentic frameworks converted to MCP Servers\n\u251c\u2500\u2500 mcpgateway # \u2190 main application package\n\u2502 \u251c\u2500\u2500 __init__.py # Package metadata & version constant\n\u2502 \u251c\u2500\u2500 admin.py # FastAPI routers for Admin UI\n\u2502 \u251c\u2500\u2500 cache\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u251c\u2500\u2500 resource_cache.py # LRU+TTL cache implementation\n\u2502 \u2502 \u2514\u2500\u2500 session_registry.py # Session \u2194 cache mapping\n\u2502 \u251c\u2500\u2500 config.py # Pydantic settings loader\n\u2502 \u251c\u2500\u2500 db.py # SQLAlchemy models & engine setup\n\u2502 \u251c\u2500\u2500 federation\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u251c\u2500\u2500 discovery.py # Peer-gateway discovery\n\u2502 \u2502 \u251c\u2500\u2500 forward.py # RPC forwarding\n\u2502 \u251c\u2500\u2500 handlers\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u2514\u2500\u2500 sampling.py # Streaming sampling handler\n\u2502 \u251c\u2500\u2500 main.py # FastAPI app factory & startup events\n\u2502 \u251c\u2500\u2500 mcp.db # SQLite fixture for tests\n\u2502 \u251c\u2500\u2500 py.typed # PEP 561 marker (ships type hints)\n\u2502 \u251c\u2500\u2500 schemas.py # Shared Pydantic DTOs\n\u2502 \u251c\u2500\u2500 services\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u251c\u2500\u2500 completion_service.py # Prompt / argument completion\n\u2502 \u2502 \u251c\u2500\u2500 gateway_service.py # Peer-gateway registry\n\u2502 \u2502 \u251c\u2500\u2500 logging_service.py # Central logging helpers\n\u2502 \u2502 \u251c\u2500\u2500 prompt_service.py # Prompt CRUD & rendering\n\u2502 \u2502 \u251c\u2500\u2500 resource_service.py # Resource registration & retrieval\n\u2502 \u2502 \u251c\u2500\u2500 root_service.py # File-system root registry\n\u2502 \u2502 \u251c\u2500\u2500 server_service.py # Server registry & monitoring\n\u2502 \u2502 \u2514\u2500\u2500 tool_service.py # Tool registry & invocation\n\u2502 \u251c\u2500\u2500 static\n\u2502 \u2502 \u251c\u2500\u2500 admin.css # Styles for Admin UI\n\u2502 \u2502 \u2514\u2500\u2500 admin.js # Behaviour for Admin UI\n\u2502 \u251c\u2500\u2500 templates\n\u2502 \u2502 \u2514\u2500\u2500 admin.html # HTMX/Alpine Admin UI template\n\u2502 \u251c\u2500\u2500 transports\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u251c\u2500\u2500 base.py # Abstract transport interface\n\u2502 \u2502 \u251c\u2500\u2500 sse_transport.py # Server-Sent Events transport\n\u2502 \u2502 \u251c\u2500\u2500 stdio_transport.py # stdio transport for embedding\n\u2502 \u2502 \u2514\u2500\u2500 websocket_transport.py # WS transport with ping/pong\n\u2502 \u251c\u2500\u2500 models.py # Core enums / type aliases\n\u2502 \u251c\u2500\u2500 utils\n\u2502 \u2502 \u251c\u2500\u2500 create_jwt_token.py # CLI & library for JWT generation\n\u2502 \u2502 \u251c\u2500\u2500 services_auth.py # Service-to-service auth dependency\n\u2502 \u2502 \u2514\u2500\u2500 verify_credentials.py # Basic / JWT auth helpers\n\u2502 \u251c\u2500\u2500 validation\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u2514\u2500\u2500 jsonrpc.py # JSON-RPC 2.0 validation\n\u2502 \u2514\u2500\u2500 version.py # Library version helper\n\u251c\u2500\u2500 mcpgateway-wrapper # Stdio client wrapper (PyPI)\n\u2502 \u251c\u2500\u2500 pyproject.toml\n\u2502 \u251c\u2500\u2500 README.md\n\u2502 \u2514\u2500\u2500 src/mcpgateway_wrapper/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2514\u2500\u2500 server.py # Wrapper entry-point\n\u251c\u2500\u2500 mcp-servers # Sample downstream MCP servers\n\u251c\u2500\u2500 mcp.db # Default SQLite DB (auto-created)\n\u251c\u2500\u2500 mcpgrid # Experimental grid client / PoC\n\u251c\u2500\u2500 os_deps.sh # Installs system-level deps for CI\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Tests & QA Assets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\u251c\u2500\u2500 test_readme.py # Guard: README stays in sync\n\u251c\u2500\u2500 tests\n\u2502 \u251c\u2500\u2500 conftest.py # Shared fixtures\n\u2502 \u251c\u2500\u2500 e2e/... # End-to-end scenarios\n\u2502 \u251c\u2500\u2500 hey/... # Load-test logs & helper script\n\u2502 \u251c\u2500\u2500 integration/... # API-level integration tests\n\u2502 \u2514\u2500\u2500 unit/... # Pure unit tests for business logic\n```\n\n</details>\n\n---\n\n## API Documentation\n\n* **Swagger UI** \u2192 [http://localhost:4444/docs](http://localhost:4444/docs)\n* **ReDoc** \u2192 [http://localhost:4444/redoc](http://localhost:4444/redoc)\n* **Admin Panel** \u2192 [http://localhost:4444/admin](http://localhost:4444/admin)\n\n---\n\n## Makefile targets\n\nThis project offer the following Makefile targets. Type `make` in the project root to show all targets.\n\n<details>\n<summary><strong>\ud83d\udd27 Available Makefile targets</strong></summary>\n\n```bash\n\ud83d\udc0d MCP CONTEXT FORGE (An enterprise-ready Model Context Protocol Gateway)\n\ud83d\udd27 SYSTEM-LEVEL DEPENDENCIES (DEV BUILD ONLY)\nos-deps - Install Graphviz, Pandoc, Trivy, SCC used for dev docs generation and security scan\n\ud83c\udf31 VIRTUAL ENVIRONMENT & INSTALLATION\nvenv - Create a fresh virtual environment with uv & friends\nactivate - Activate the virtual environment in the current shell\ninstall - Install project into the venv\ninstall-dev - Install project (incl. dev deps) into the venv\ninstall-db - Install project (incl. postgres and redis) into venv\nupdate - Update all installed deps inside the venv\ncheck-env - Verify all required env vars in .env are present\n\u25b6\ufe0f SERVE & TESTING\nserve - Run production Gunicorn server on :4444\ncerts - Generate self-signed TLS cert & key in ./certs (won't overwrite)\nserve-ssl - Run Gunicorn behind HTTPS on :4444 (uses ./certs)\ndev - Run fast-reload dev server (uvicorn)\nrun - Execute helper script ./run.sh\ntest - Run unit tests with pytest\ntest-curl - Smoke-test API endpoints with curl script\npytest-examples - Run README / examples through pytest-examples\nclean - Remove caches, build artefacts, virtualenv, docs, certs, coverage, SBOM, etc.\n\ud83d\udcca COVERAGE & METRICS\ncoverage - Run tests with coverage, emit md/HTML/XML + badge\npip-licenses - Produce dependency license inventory (markdown)\nscc - Quick LoC/complexity snapshot with scc\nscc-report - Generate HTML LoC & per-file metrics with scc\n\ud83d\udcda DOCUMENTATION & SBOM\ndocs - Build docs (graphviz + handsdown + images + SBOM)\nimages - Generate architecture & dependency diagrams\n\ud83d\udd0d LINTING & STATIC ANALYSIS\nlint - Run the full linting suite (see targets below)\nblack - Reformat code with black\nautoflake - Remove unused imports / variables with autoflake\nisort - Organise & sort imports with isort\nflake8 - PEP-8 style & logical errors\npylint - Pylint static analysis\nmarkdownlint - Lint Markdown files with markdownlint (requires markdownlint-cli)\nmypy - Static type-checking with mypy\nbandit - Security scan with bandit\npydocstyle - Docstring style checker\npycodestyle - Simple PEP-8 checker\npre-commit - Run all configured pre-commit hooks\nruff - Ruff linter + formatter\nty - Ty type checker from astral\npyright - Static type-checking with Pyright\nradon - Code complexity & maintainability metrics\npyroma - Validate packaging metadata\nimportchecker - Detect orphaned imports\nspellcheck - Spell-check the codebase\nfawltydeps - Detect undeclared / unused deps\nwily - Maintainability report\npyre - Static analysis with Facebook Pyre\ndepend - List dependencies in \u2248requirements format\nsnakeviz - Profile & visualise with snakeviz\npstats - Generate PNG call-graph from cProfile stats\nspellcheck-sort - Sort local spellcheck dictionary\ntox - Run tox across multi-Python versions\nsbom - Produce a CycloneDX SBOM and vulnerability scan\npytype - Flow-sensitive type checker\ncheck-manifest - Verify sdist/wheel completeness\nyamllint - Lint YAML files (uses .yamllint)\njsonlint - Validate every *.json file with jq (--exit-status)\ntomllint - Validate *.toml files with tomlcheck\n\ud83d\udd78\ufe0f WEBPAGE LINTERS & STATIC ANALYSIS (HTML/CSS/JS lint + security scans + formatting)\ninstall-web-linters - Install HTMLHint, Stylelint, ESLint, Retire.js & Prettier via npm\nlint-web - Run HTMLHint, Stylelint, ESLint, Retire.js and npm audit\nformat-web - Format HTML, CSS & JS files with Prettier\nosv-install - Install/upgrade osv-scanner (Go)\nosv-scan-source - Scan source & lockfiles for CVEs\nosv-scan-image - Scan the built container image for CVEs\nosv-scan - Run all osv-scanner checks (source, image, licence)\n\ud83d\udce1 SONARQUBE ANALYSIS\nsonar-deps-podman - Install podman-compose + supporting tools\nsonar-deps-docker - Install docker-compose + supporting tools\nsonar-up-podman - Launch SonarQube with podman-compose\nsonar-up-docker - Launch SonarQube with docker-compose\nsonar-submit-docker - Run containerised Sonar Scanner CLI with Docker\nsonar-submit-podman - Run containerised Sonar Scanner CLI with Podman\npysonar-scanner - Run scan with Python wrapper (pysonar-scanner)\nsonar-info - How to create a token & which env vars to export\n\ud83d\udee1\ufe0f SECURITY & PACKAGE SCANNING\ntrivy - Scan container image for CVEs (HIGH/CRIT). Needs podman socket enabled\ndockle - Lint the built container image via tarball (no daemon/socket needed)\nhadolint - Lint Containerfile/Dockerfile(s) with hadolint\npip-audit - Audit Python dependencies for published CVEs\n\ud83d\udce6 DEPENDENCY MANAGEMENT\ndeps-update - Run update-deps.py to update all dependencies in pyproject.toml and docs/requirements.txt\ncontainerfile-update - Update base image in Containerfile to latest tag\n\ud83d\udce6 PACKAGING & PUBLISHING\ndist - Clean-build wheel *and* sdist into ./dist\nwheel - Build wheel only\nsdist - Build source distribution only\nverify - Build + twine + check-manifest + pyroma (no upload)\npublish - Verify, then upload to PyPI (needs TWINE_* creds)\n\ud83e\uddad PODMAN CONTAINER BUILD & RUN\npodman-dev - Build development container image\npodman - Build container image\npodman-prod - Build production container image (using ubi-micro \u2192 scratch). Not supported on macOS.\npodman-run - Run the container on HTTP (port 4444)\npodman-run-shell - Run the container on HTTP (port 4444) and start a shell\npodman-run-ssl - Run the container on HTTPS (port 4444, self-signed)\npodman-run-ssl-host - Run the container on HTTPS with --network=host (port 4444, self-signed)\npodman-stop - Stop & remove the container\npodman-test - Quick curl smoke-test against the container\npodman-logs - Follow container logs (\u2303C to quit)\npodman-stats - Show container resource stats (if supported)\npodman-top - Show live top-level process info in container\npodman-shell - Open an interactive shell inside the Podman container\n\ud83d\udc0b DOCKER BUILD & RUN\ndocker-dev - Build development Docker image\ndocker - Build production Docker image\ndocker-prod - Build production container image (using ubi-micro \u2192 scratch). Not supported on macOS.\ndocker-run - Run the container on HTTP (port 4444)\ndocker-run-ssl - Run the container on HTTPS (port 4444, self-signed)\ndocker-stop - Stop & remove the container\ndocker-test - Quick curl smoke-test against the container\ndocker-logs - Follow container logs (\u2303C to quit)\ndocker-stats - Show container resource usage stats (non-streaming)\ndocker-top - Show top-level process info in Docker container\ndocker-shell - Open an interactive shell inside the Docker container\n\ud83d\udee0\ufe0f COMPOSE STACK - Build / start / stop the multi-service stack\ncompose-up - Bring the whole stack up (detached)\ncompose-restart - Recreate changed containers, pulling / building as needed\ncompose-build - Build (or rebuild) images defined in the compose file\ncompose-pull - Pull the latest images only\ncompose-logs - Tail logs from all services (Ctrl-C to exit)\ncompose-ps - Show container status table\ncompose-shell - Open an interactive shell in the \"gateway\" container\ncompose-stop - Gracefully stop the stack (keep containers)\ncompose-down - Stop & remove containers (keep named volumes)\ncompose-rm - Remove *stopped* containers\ncompose-clean - \u2728 Down **and** delete named volumes (data-loss \u26a0)\n\u2601\ufe0f IBM CLOUD CODE ENGINE\nibmcloud-check-env - Verify all required IBM Cloud env vars are set\nibmcloud-cli-install - Auto-install IBM Cloud CLI + required plugins (OS auto-detected)\nibmcloud-login - Login to IBM Cloud CLI using IBMCLOUD_API_KEY (--sso)\nibmcloud-ce-login - Set Code Engine target project and region\nibmcloud-list-containers - List deployed Code Engine apps\nibmcloud-tag - Tag container image for IBM Container Registry\nibmcloud-push - Push image to IBM Container Registry\nibmcloud-deploy - Deploy (or update) container image in Code Engine\nibmcloud-ce-logs - Stream logs for the deployed application\nibmcloud-ce-status - Get deployment status\nibmcloud-ce-rm - Delete the Code Engine application\n\ud83e\uddea MINIKUBE LOCAL CLUSTER\nminikube-install - Install Minikube (macOS, Linux, or Windows via choco)\nhelm-install - Install Helm CLI (macOS, Linux, or Windows)\nminikube-start - Start local Minikube cluster with Ingress + DNS + metrics-server\nminikube-stop - Stop the Minikube cluster\nminikube-delete - Delete the Minikube cluster\nminikube-image-load - Build and load ghcr.io/ibm/mcp-context-forge:latest into Minikube\nminikube-k8s-apply - Apply Kubernetes manifests from k8s/\nminikube-status - Show status of Minikube and ingress pods\n\ud83d\udee0\ufe0f HELM CHART TASKS\nhelm-lint - Lint the Helm chart (static analysis)\nhelm-package - Package the chart into dist/ as mcp-stack-<ver>.tgz\nhelm-deploy - Upgrade/Install chart into Minikube (profile mcpgw)\nhelm-delete - Uninstall the chart release from Minikube\n\ud83c\udfe0 LOCAL PYPI SERVER\nlocal-pypi-install - Install pypiserver for local testing\nlocal-pypi-start - Start local PyPI server on :8084 (no auth)\nlocal-pypi-start-auth - Start local PyPI server with basic auth (admin/admin)\nlocal-pypi-stop - Stop local PyPI server\nlocal-pypi-upload - Upload existing package to local PyPI (no auth)\nlocal-pypi-upload-auth - Upload existing package to local PyPI (with auth)\nlocal-pypi-test - Install package from local PyPI\nlocal-pypi-clean - Full cycle: build \u2192 upload \u2192 install locally\n\ud83c\udfe0 LOCAL DEVPI SERVER\ndevpi-install - Install devpi server and client\ndevpi-init - Initialize devpi server (first time only)\ndevpi-start - Start devpi server\ndevpi-stop - Stop devpi server\ndevpi-setup-user - Create user and dev index\ndevpi-upload - Upload existing package to devpi\ndevpi-test - Install package from devpi\ndevpi-clean - Full cycle: build \u2192 upload \u2192 install locally\ndevpi-status - Show devpi server status\ndevpi-web - Open devpi web interface\n```\n</details>\n\n## \ud83d\udd0d Troubleshooting\n\n<details>\n<summary><strong>Port publishing on WSL2 (rootless Podman & Docker Desktop)</strong></summary>\n\n### Diagnose the listener\n\n```bash\n# Inside your WSL distro\nss -tlnp | grep 4444 # Use ss\nnetstat -anp | grep 4444 # or netstat\n```\n\n*Seeing `:::4444 LISTEN rootlessport` is normal* - the IPv6 wildcard\nsocket (`::`) also accepts IPv4 traffic **when**\n`net.ipv6.bindv6only = 0` (default on Linux).\n\n### Why localhost fails on Windows\n\nWSL 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.\n\n#### Fix (Podman rootless)\n\n```bash\n# Inside the WSL distro\necho \"wsl\" | sudo tee /etc/containers/podman-machine\nsystemctl --user restart podman.socket\n```\n\n`ss` should now show `0.0.0.0:4444` instead of `:::4444`, and the\nservice becomes reachable from Windows *and* the LAN.\n\n#### Fix (Docker Desktop > 4.19)\n\nDocker Desktop adds a \"WSL integration\" switch per-distro.\nTurn it **on** for your distro, restart Docker Desktop, then restart the\ncontainer:\n\n```bash\ndocker restart mcpgateway\n```\n\n</details>\n\n<details>\n<summary><strong>Gateway starts but immediately exits (\"Failed to read DATABASE_URL\")</strong></summary>\n\nCopy `.env.example` to `.env` first:\n\n```bash\ncp .env.example .env\n```\n\nThen edit `DATABASE_URL`, `JWT_SECRET_KEY`, `BASIC_AUTH_PASSWORD`, etc.\nMissing or empty required vars cause a fast-fail at startup.\n\n</details>\n\n## Contributing\n\n1. Fork the repo, create a feature branch.\n2. Run `make lint` and fix any issues.\n3. Keep `make test` green and 100% coverage.\n4. Open a PR - describe your changes clearly.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for more details.\n---\n\n## Changelog\n\nA complete changelog can be found here: [CHANGELOG.md](./CHANGELOG.md)\n\n## License\n\nLicensed under the **Apache License 2.0** - see [LICENSE](./LICENSE)\n\n\n## Core Authors and Maintainers\n\n- [Mihai Criveti](https://www.linkedin.com/in/crivetimihai) - Distinguished Engineer, Agentic AI\n\nSpecial thanks to our contributors for helping us improve ContextForge MCP Gateway:\n\n<a href=\"https://github.com/ibm/mcp-context-forge/graphs/contributors\">\n <img src=\"https://contrib.rocks/image?repo=ibm/mcp-context-forge&max=100&anon=0&columns=10\" />\n</a>\n\n## Star History and Project Activity\n\n[](https://www.star-history.com/#ibm/mcp-context-forge&Date)\n\n<!-- === Usage Stats === -->\n[](https://pepy.tech/project/mcp-contextforge-gateway) \n[](https://github.com/ibm/mcp-context-forge/stargazers) \n[](https://github.com/ibm/mcp-context-forge/network/members) \n[](https://github.com/ibm/mcp-context-forge/graphs/contributors) \n[](https://github.com/ibm/mcp-context-forge/commits) \n[](https://github.com/ibm/mcp-context-forge/issues) \n",
"bugtrack_url": null,
"license": null,
"summary": "A production-grade MCP Gateway & Proxy built with FastAPI. Supports multi-server registration, virtual server composition, authentication, retry logic, observability, protocol translation, and a unified federated tool catalog.",
"version": "0.3.1",
"project_urls": {
"Bug Tracker": "https://github.com/IBM/mcp-context-forge/issues",
"Changelog": "https://github.com/IBM/mcp-context-forge/blob/main/CHANGELOG.md",
"Documentation": "https://ibm.github.io/mcp-context-forge/",
"Homepage": "https://ibm.github.io/mcp-context-forge/",
"Repository": "https://github.com/IBM/mcp-context-forge"
},
"split_keywords": [
"mcp",
" api",
" gateway",
" proxy",
" tools",
" agents",
" agentic ai",
" model context protocol",
" multi-agent",
" fastapi",
" json-rpc",
" sse",
" websocket",
" federation",
" security",
" authentication"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "b201eb5abca4512ce20dbbca7f309275e3e7f1b322493b2ce8eb7b1854301435",
"md5": "370226f22d2c5f3e09ab4ccd3a43ef4b",
"sha256": "0c360103e83f5caf93b62a28a2d303668506ee1f732b6c1f0b887e32c6b8341e"
},
"downloads": -1,
"filename": "mcp_contextforge_gateway-0.3.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "370226f22d2c5f3e09ab4ccd3a43ef4b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<3.14,>=3.11",
"size": 252701,
"upload_time": "2025-07-11T03:26:40",
"upload_time_iso_8601": "2025-07-11T03:26:40.738144Z",
"url": "https://files.pythonhosted.org/packages/b2/01/eb5abca4512ce20dbbca7f309275e3e7f1b322493b2ce8eb7b1854301435/mcp_contextforge_gateway-0.3.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "e11e8a30fb7a476dc481d4c9d99d1a574d80bcd5b40bb75a5f4643ad82d0d8d4",
"md5": "a64941ff85ef97d17fd0a095d4eafb8e",
"sha256": "b91250975881b0565f579a4a8f508ff946c9b16e8ac56ade3231653cc8bdbd10"
},
"downloads": -1,
"filename": "mcp_contextforge_gateway-0.3.1.tar.gz",
"has_sig": false,
"md5_digest": "a64941ff85ef97d17fd0a095d4eafb8e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<3.14,>=3.11",
"size": 3129664,
"upload_time": "2025-07-11T03:26:52",
"upload_time_iso_8601": "2025-07-11T03:26:52.109169Z",
"url": "https://files.pythonhosted.org/packages/e1/1e/8a30fb7a476dc481d4c9d99d1a574d80bcd5b40bb75a5f4643ad82d0d8d4/mcp_contextforge_gateway-0.3.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-11 03:26:52",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "IBM",
"github_project": "mcp-context-forge",
"travis_ci": true,
"coveralls": true,
"github_actions": true,
"tox": true,
"lcname": "mcp-contextforge-gateway"
}