mcp-contextforge-gateway


Namemcp-contextforge-gateway JSON
Version 0.6.0 PyPI version JSON
download
home_pageNone
SummaryA 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.
upload_time2025-08-22 19:04:50
maintainerNone
docs_urlNone
authorNone
requires_python<3.14,>=3.11
licenseNone
keywords mcp api gateway proxy tools agents agentic ai model context protocol multi-agent fastapi json-rpc sse websocket federation security authentication
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage
            # MCP Gateway

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

![](docs/docs/images/contextforge-banner.png)

<!-- === CI / Security / Build Badges === -->
[![Build Python Package](https://github.com/IBM/mcp-context-forge/actions/workflows/python-package.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/python-package.yml)&nbsp;
[![CodeQL](https://github.com/IBM/mcp-context-forge/actions/workflows/codeql.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/codeql.yml)&nbsp;
[![Bandit Security](https://github.com/IBM/mcp-context-forge/actions/workflows/bandit.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/bandit.yml)&nbsp;
[![Dependency Review](https://github.com/IBM/mcp-context-forge/actions/workflows/dependency-review.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/dependency-review.yml)&nbsp;
[![Tests & Coverage](https://github.com/IBM/mcp-context-forge/actions/workflows/pytest.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/pytest.yml)&nbsp;
[![Lint & Static Analysis](https://github.com/IBM/mcp-context-forge/actions/workflows/lint.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/lint.yml)

<!-- === Container Build & Deploy === -->
[![Secure Docker Build](https://github.com/IBM/mcp-context-forge/actions/workflows/docker-image.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/docker-image.yml)&nbsp;
[![Deploy to IBM Code Engine](https://github.com/IBM/mcp-context-forge/actions/workflows/ibm-cloud-code-engine.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/ibm-cloud-code-engine.yml)

<!-- === Package / Container === -->
[![Async](https://img.shields.io/badge/async-await-green.svg)](https://docs.python.org/3/library/asyncio.html)
[![License](https://img.shields.io/github/license/ibm/mcp-context-forge)](LICENSE)&nbsp;
[![PyPI](https://img.shields.io/pypi/v/mcp-contextforge-gateway)](https://pypi.org/project/mcp-contextforge-gateway/)&nbsp;
[![Docker Image](https://img.shields.io/badge/docker-ghcr.io%2Fibm%2Fmcp--context--forge-blue)](https://github.com/ibm/mcp-context-forge/pkgs/container/mcp-context-forge)&nbsp;


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.

![MCP Gateway](https://ibm.github.io/mcp-context-forge/images/mcpgateway.gif)
---

<!-- 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. [Containerized (self-signed TLS)](#containerized-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 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.

**โš ๏ธ Caution**: The current release (0.6.0) is considered alpha / early beta. It is not production-ready and should only be used for local development, testing, or experimentation. Features, APIs, and behaviors are subject to change without notice. **Do not** deploy in production environments without thorough security review, validation and additional security mechanisms.  Many of the features required for secure, large-scale, or multi-tenant production deployments are still on the [project roadmap](https://ibm.github.io/mcp-context-forge/architecture/roadmap/) - which is itself evolving.

It currently supports:

* Federation across multiple MCP and REST services
* **A2A (Agent-to-Agent) integration** for external AI agents (OpenAI, Anthropic, custom)
* Virtualization of legacy APIs as MCP-compliant tools and servers
* Transport over HTTP, JSON-RPC, WebSocket, SSE (with configurable keepalive), stdio and streamable-HTTP
* An Admin UI for real-time management, configuration, and log monitoring
* Built-in auth, retries, and rate-limiting
* **OpenTelemetry observability** with Phoenix, Jaeger, Zipkin, and other OTLP backends
* Scalable deployments via Docker or PyPI, Redis-backed caching, and multi-cluster federation

![MCP Gateway Architecture](https://ibm.github.io/mcp-context-forge/images/mcpgateway.svg)

For a list of upcoming features, check out the [ContextForge MCP Gateway Roadmap](https://ibm.github.io/mcp-context-forge/architecture/roadmap/)

**โš ๏ธ Important**: MCP Gateway is not a standalone product - it is an open source component with **NO OFFICIAL SUPPORT** from IBM or its affiliates that can be integrated into your own solution architecture. If you choose to use it, you are responsible for evaluating its fit, securing the deployment, and managing its lifecycle. See [SECURITY.md](./SECURITY.md) for more details.

---

<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
* Real-time log viewer with filtering, search, and export capabilities
* Auth: Basic, JWT, or custom schemes
* Structured logs, health endpoints, metrics
* 400+ tests, Makefile targets, live reload, pre-commit hooks

</details>

<details>
<summary><strong>๐Ÿ” OpenTelemetry Observability</strong></summary>

* **Vendor-agnostic tracing** with OpenTelemetry (OTLP) protocol support
* **Multiple backend support**: Phoenix (LLM-focused), Jaeger, Zipkin, Tempo, DataDog, New Relic
* **Distributed tracing** across federated gateways and services
* **Automatic instrumentation** of tools, prompts, resources, and gateway operations
* **LLM-specific metrics**: Token usage, costs, model performance
* **Zero-overhead when disabled** with graceful degradation
* **Easy configuration** via environment variables

Quick start with Phoenix (LLM observability):
```bash
# Start Phoenix
docker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latest

# Configure gateway
export OTEL_ENABLE_OBSERVABILITY=true
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

# Run gateway - traces automatically sent to Phoenix
mcpgateway
```

See [Observability Documentation](https://ibm.github.io/mcp-context-forge/manage/observability/) for detailed setup with other backends.

</details>

---

## Quick Start - PyPI

MCP Gateway is published on [PyPI](https://pypi.org/project/mcp-contextforge-gateway/) as `mcp-contextforge-gateway`.

---

**TLDR;**:
(single command using [uv](https://docs.astral.sh/uv/))

```bash
BASIC_AUTH_PASSWORD=pass \
MCPGATEWAY_UI_ENABLED=true \
MCPGATEWAY_ADMIN_API_ENABLED=true \
uvx --from mcp-contextforge-gateway mcpgateway --host 0.0.0.0 --port 4444
```

<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>Windows (PowerShell) quick-start</strong></summary>

```powershell
# 1๏ธโƒฃ  Isolated env + install from PyPI
mkdir mcpgateway ; cd mcpgateway
python3 -m venv .venv ; .\.venv\Scripts\Activate.ps1
pip install --upgrade pip
pip install mcp-contextforge-gateway

# 2๏ธโƒฃ  Environment variables (session-only)
$Env:MCPGATEWAY_UI_ENABLED        = "true"
$Env:MCPGATEWAY_ADMIN_API_ENABLED = "true"
$Env:BASIC_AUTH_PASSWORD          = "changeme"      # admin/changeme
$Env:JWT_SECRET_KEY               = "my-test-key"

# 3๏ธโƒฃ  Launch the gateway
mcpgateway.exe --host 0.0.0.0 --port 4444

#   Optional: background it
# Start-Process -FilePath "mcpgateway.exe" -ArgumentList "--host 0.0.0.0 --port 4444"

# 4๏ธโƒฃ  Bearer token and smoke-test
$Env:MCPGATEWAY_BEARER_TOKEN = python3 -m mcpgateway.utils.create_jwt_token `
    --username admin --exp 10080 --secret my-test-key

curl -s -H "Authorization: Bearer $Env:MCPGATEWAY_BEARER_TOKEN" `
     http://127.0.0.1:4444/version | jq
```

</details>

<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" \
     --expose-sse \
     --port 8003

# Or using the official mcp-server-git using uvx:
pip install uv # to install uvx, if not already installed
python3 -m mcpgateway.translate --stdio "uvx mcp-server-git" --expose-sse --port 9000

# Alternative: running the local binary
# cd mcp-servers/go/fast-time-server; make build
# python3 -m mcpgateway.translate --stdio "./dist/fast-time-server -transport=stdio" --expose-sse --port 8002

# NEW: Expose via multiple protocols simultaneously!
python3 -m mcpgateway.translate \
     --stdio "uvx mcp-server-git" \
     --expose-sse \
     --expose-streamable-http \
     --port 9000
# Now accessible via both /sse (SSE) and /mcp (streamable HTTP) endpoints

# 2๏ธโƒฃ  Register it with the gateway
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"name":"fast_time","url":"http://localhost:9000/sse"}' \
     http://localhost:4444/gateways

# 3๏ธโƒฃ  Verify tool catalog
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/tools | jq

# 4๏ธโƒฃ  Create a *virtual server* bundling those tools. Use the ID of tools from the tool catalog (Step #3) and pass them in the associatedTools list.
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"name":"time_server","description":"Fast time tools","associatedTools":[<ID_OF_TOOLS>]}' \
     http://localhost:4444/servers | jq

# Example curl
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN"
     -H "Content-Type: application/json"
     -d '{"name":"time_server","description":"Fast time tools","associatedTools":["6018ca46d32a4ac6b4c054c13a1726a2"]}' \
     http://localhost:4444/servers | jq

# 5๏ธโƒฃ  List servers (should now include the UUID of the newly created virtual server)
curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/servers | jq

# 6๏ธโƒฃ  Client SSE endpoint. Inspect it interactively with the MCP Inspector CLI (or use any MCP client)
npx -y @modelcontextprotocol/inspector
# Transport Type: SSE, URL: http://localhost:4444/servers/UUID_OF_SERVER_1/sse,  Header Name: "Authorization", Bearer Token
```

</details>

<details>
<summary><strong>๐Ÿ–ง Using the stdio wrapper (mcpgateway-wrapper)</strong></summary>

```bash
export MCP_AUTH=$MCPGATEWAY_BEARER_TOKEN
export MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp
python3 -m mcpgateway.wrapper  # Ctrl-C to exit
```

You can also run it with `uv` or inside Docker/Podman - see the *Containers* section above.

In MCP Inspector, define `MCP_AUTH` and `MCP_SERVER_URL` env variables, and select `python3` as the Command, and `-m mcpgateway.wrapper` as Arguments.

```bash
echo $PWD/.venv/bin/python3 # Using the Python3 full path ensures you have a working venv
export MCP_SERVER_URL='http://localhost:4444/servers/UUID_OF_SERVER_1/mcp'
export MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN}
npx -y @modelcontextprotocol/inspector
```

or

Pass the url and auth as arguments (no need to set environment variables)
```bash
npx -y @modelcontextprotocol/inspector
command as `python`
Arguments as `-m mcpgateway.wrapper --url "http://localhost:4444/servers/UUID_OF_SERVER_1/mcp" --auth "Bearer <your token>"`
```


When using a MCP Client such as Claude with stdio:

```json
{
  "mcpServers": {
    "mcpgateway-wrapper": {
      "command": "python",
      "args": ["-m", "mcpgateway.wrapper"],
      "env": {
        "MCP_AUTH": "your-token-here",
        "MCP_SERVER_URL": "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 MCPGATEWAY_UI_ENABLED=true \
  -e MCPGATEWAY_ADMIN_API_ENABLED=true \
  -e HOST=0.0.0.0 \
  -e JWT_SECRET_KEY=my-test-key \
  -e BASIC_AUTH_USER=admin \
  -e BASIC_AUTH_PASSWORD=changeme \
  -e AUTH_REQUIRED=true \
  -e DATABASE_URL=sqlite:///./mcp.db \
  ghcr.io/ibm/mcp-context-forge:0.6.0

# Tail logs (Ctrl+C to quit)
docker logs -f mcpgateway

# Generating an API key
docker run --rm -it ghcr.io/ibm/mcp-context-forge:0.6.0 \
  python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 0 --secret my-test-key
```

Browse to **[http://localhost:4444/admin](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 MCPGATEWAY_UI_ENABLED=true \
  -e MCPGATEWAY_ADMIN_API_ENABLED=true \
  -e DATABASE_URL=sqlite:////data/mcp.db \
  -e HOST=0.0.0.0 \
  -e JWT_SECRET_KEY=my-test-key \
  -e BASIC_AUTH_USER=admin \
  -e BASIC_AUTH_PASSWORD=changeme \
  ghcr.io/ibm/mcp-context-forge:0.6.0
```

SQLite now lives on the host at `./data/mcp.db`.

#### 3 - Local tool discovery (host network)

```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 MCPGATEWAY_UI_ENABLED=true \
  -e MCPGATEWAY_ADMIN_API_ENABLED=true \
  -e HOST=0.0.0.0 \
  -e PORT=4444 \
  -e DATABASE_URL=sqlite:////data/mcp.db \
  -v $(pwd)/data:/data \
  ghcr.io/ibm/mcp-context-forge:0.6.0
```

Using `--network=host` allows Docker to access the local network, allowing you to add MCP servers running on your host. See [Docker Host network driver documentation](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.6.0
```

#### 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.6.0
```

#### 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.6.0
```

---

<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.6.0`) 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=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_URL='http://localhost:4444/servers/UUID_OF_SERVER_1/mcp'
export MCP_TOOL_CALL_TIMEOUT=120
export MCP_WRAPPER_LOG_LEVEL=DEBUG  # or OFF to disable logging

docker run --rm -i \
  -e MCP_AUTH=$MCPGATEWAY_BEARER_TOKEN \
  -e MCP_SERVER_URL=http://host.docker.internal:4444/servers/UUID_OF_SERVER_1/mcp \
  -e MCP_TOOL_CALL_TIMEOUT=120 \
  -e MCP_WRAPPER_LOG_LEVEL=DEBUG \
  ghcr.io/ibm/mcp-context-forge:0.6.0 \
  python3 -m mcpgateway.wrapper
```

</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=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_URL=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.6.0"}}}

# When there's no tools
{"jsonrpc":"2.0","id":2,"result":{"tools":[]}}

# After you add some tools and create a virtual server
{"jsonrpc":"2.0","id":2,"result":{"tools":[{"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":true},"description":"Convert time between different timezones","inputSchema":{"properties":{"source_timezone":{"description":"Source IANA timezone name","type":"string"},"target_timezone":{"description":"Target IANA timezone name","type":"string"},"time":{"description":"Time to convert in RFC3339 format or common formats like '2006-01-02 15:04:05'","type":"string"}},"required":["time","source_timezone","target_timezone"],"type":"object"},"name":"convert_time"},{"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":true},"description":"Get current system time in specified timezone","inputSchema":{"properties":{"timezone":{"description":"IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Defaults to UTC","type":"string"}},"type":"object"},"name":"get_system_time"}]}}

# Running the time tool:
{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"2025-07-09T00:09:45+01:00"}]}}
```

</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/mcp`.
> When inside Docker/Podman, that often becomes `http://host.docker.internal:4444/servers/UUID_OF_SERVER_1/mcp` (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_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \
  -e MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN} \
  -e MCP_TOOL_CALL_TIMEOUT=120 \
  ghcr.io/ibm/mcp-context-forge:0.6.0 \
  python3 -m mcpgateway.wrapper
```

</details>

---

<details>
<summary><strong>๐Ÿ“ฆ pipx (one-liner install &amp; 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=${MCPGATEWAY_BEARER_TOKEN} \
MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \
python3 -m mcpgateway.wrapper
# Alternatively with uv
uv run --directory . -m mcpgateway.wrapper
```

**Claude Desktop JSON** (uses the host Python that pipx injected):

```json
{
  "mcpServers": {
    "mcpgateway-wrapper": {
      "command": "python3",
      "args": ["-m", "mcpgateway.wrapper"],
      "env": {
        "MCP_AUTH": "<your-token>",
        "MCP_SERVER_URL": "http://localhost:4444/servers/UUID_OF_SERVER_1/mcp",
        "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=${MCPGATEWAY_BEARER_TOKEN} \
MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \
uv run --directory . -m mcpgateway.wrapper # Use this just for testing, as the Client will run the uv command
```

#### Claude Desktop JSON (runs through **uvx**)

```json
{
  "mcpServers": {
    "mcpgateway-wrapper": {
      "command": "uvx",
      "args": [
        "run",
        "--",
        "python",
        "-m",
        "mcpgateway.wrapper"
      ],
      "env": {
        "MCP_AUTH": "<your-token>",
        "MCP_SERVER_URL": "http://localhost:4444/servers/UUID_OF_SERVER_1/mcp"
    }
  }
}
```

</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 containerized 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.)
```

### Containerized (self-signed TLS)

## Container Runtime Support

This project supports both Docker and Podman. The Makefile automatically detects
which runtime is available and handles image naming differences.

### Auto-detection
```bash
make container-build  # Uses podman if available, otherwise docker

> You can use docker or podman, ex:

```bash
make podman            # build production image
make podman-run-ssl    # run at https://localhost:4444
# or listen on port 4444 on your host directly, adds --network=host to podman
make podman-run-ssl-host
```

### Smoke-test the API

```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                 | `127.0.0.1`            | IPv4/IPv6              |
| `PORT`          | Port the server listens on               | `4444`                 | 1-65535                |
| `DATABASE_URL`  | SQLAlchemy connection URL                | `sqlite:///./mcp.db`   | any SQLAlchemy dialect |
| `APP_ROOT_PATH` | Subpath prefix for app (e.g. `/gateway`) | (empty)                | string                 |
| `TEMPLATES_DIR` | Path to Jinja2 templates                 | `mcpgateway/templates` | path                   |
| `STATIC_DIR`    | Path to static files                     | `mcpgateway/static`    | path                   |

> ๐Ÿ’ก Use `APP_ROOT_PATH=/foo` if reverse-proxying under a subpath like `https://host.com/foo/`.

### Authentication

| Setting               | Description                                                      | Default       | Options    |
| --------------------- | ---------------------------------------------------------------- | ------------- | ---------- |
| `BASIC_AUTH_USER`     | Username for Admin UI login and HTTP Basic authentication        | `admin`       | string     |
| `BASIC_AUTH_PASSWORD` | Password for Admin UI login and HTTP Basic authentication        | `changeme`    | string     |
| `AUTH_REQUIRED`       | Require authentication for all API routes                        | `true`        | bool       |
| `JWT_SECRET_KEY`      | Secret key used to **sign JWT tokens** for API access            | `my-test-key` | string     |
| `JWT_ALGORITHM`       | Algorithm used to sign the JWTs (`HS256` is default, HMAC-based) | `HS256`       | PyJWT algs |
| `TOKEN_EXPIRY`        | Expiry of generated JWTs in minutes                              | `10080`       | int > 0    |
| `AUTH_ENCRYPTION_SECRET` | Passphrase used to derive AES key for encrypting tool auth headers | `my-test-salt` | string |

> ๐Ÿ” `BASIC_AUTH_USER`/`PASSWORD` are used for:
>
> * Logging into the web-based Admin UI
> * Accessing APIs via Basic Auth (`curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN"`)
>
> ๐Ÿ”‘ `JWT_SECRET_KEY` is used to:
>
> * Sign JSON Web Tokens (`Authorization: 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 | `false` | bool    |
| `MCPGATEWAY_ADMIN_API_ENABLED` | Enable API endpoints for admin ops     | `false` | bool    |
| `MCPGATEWAY_BULK_IMPORT_ENABLED` | Enable bulk import endpoint for tools | `true`  | bool    |

> ๐Ÿ–ฅ๏ธ Set both UI and Admin API to `false` to disable management UI and APIs in production.
> ๐Ÿ“ฅ The bulk import endpoint allows importing up to 200 tools in a single request via `/admin/tools/import`.

### A2A (Agent-to-Agent) Features

| Setting                        | Description                            | Default | Options |
| ------------------------------ | -------------------------------------- | ------- | ------- |
| `MCPGATEWAY_A2A_ENABLED`       | Enable A2A agent features             | `true`  | bool    |
| `MCPGATEWAY_A2A_MAX_AGENTS`    | Maximum number of A2A agents allowed  | `100`   | int     |
| `MCPGATEWAY_A2A_DEFAULT_TIMEOUT` | Default timeout for A2A HTTP requests (seconds) | `30` | int |
| `MCPGATEWAY_A2A_MAX_RETRIES`   | Maximum retry attempts for A2A calls  | `3`     | int     |
| `MCPGATEWAY_A2A_METRICS_ENABLED` | Enable A2A agent metrics collection | `true`  | bool    |

> ๐Ÿค– **A2A Integration**: Register external AI agents (OpenAI, Anthropic, custom) and expose them as MCP tools
> ๐Ÿ“Š **Metrics**: Track agent performance, success rates, and response times
> ๐Ÿ”’ **Security**: Encrypted credential storage and configurable authentication
> ๐ŸŽ›๏ธ **Admin UI**: Dedicated tab for agent management with test functionality

**A2A Configuration Effects:**
- `MCPGATEWAY_A2A_ENABLED=false`: Completely disables A2A features (API endpoints return 404, admin tab hidden)
- `MCPGATEWAY_A2A_METRICS_ENABLED=false`: Disables metrics collection while keeping functionality

### Security

| Setting                   | Description                    | Default                                        | Options    |
| ------------------------- | ------------------------------ | ---------------------------------------------- | ---------- |
| `SKIP_SSL_VERIFY`         | Skip upstream TLS verification | `false`                                        | bool       |
| `ENVIRONMENT`             | Deployment environment (affects security defaults) | `development`                              | `development`/`production` |
| `APP_DOMAIN`              | Domain for production CORS origins | `localhost`                                 | string     |
| `ALLOWED_ORIGINS`         | CORS allow-list                | Auto-configured by environment                 | JSON array |
| `CORS_ENABLED`            | Enable CORS                    | `true`                                         | bool       |
| `CORS_ALLOW_CREDENTIALS`  | Allow credentials in CORS      | `true`                                         | bool       |
| `SECURE_COOKIES`          | Force secure cookie flags     | `true`                                         | bool       |
| `COOKIE_SAMESITE`         | Cookie SameSite attribute      | `lax`                                          | `strict`/`lax`/`none` |
| `SECURITY_HEADERS_ENABLED` | Enable security headers middleware | `true`                                     | bool       |
| `X_FRAME_OPTIONS`         | X-Frame-Options header value   | `DENY`                                         | `DENY`/`SAMEORIGIN` |
| `HSTS_ENABLED`            | Enable HSTS header             | `true`                                         | bool       |
| `HSTS_MAX_AGE`            | HSTS max age in seconds        | `31536000`                                     | int        |
| `REMOVE_SERVER_HEADERS`   | Remove server identification   | `true`                                         | bool       |
| `DOCS_ALLOW_BASIC_AUTH`   | Allow Basic Auth for docs (in addition to JWT)         | `false`                                        | bool       |

> **CORS Configuration**: When `ENVIRONMENT=development`, CORS origins are automatically configured for common development ports (3000, 8080, gateway port). In production, origins are constructed from `APP_DOMAIN` (e.g., `https://yourdomain.com`, `https://app.yourdomain.com`). You can override this by explicitly setting `ALLOWED_ORIGINS`.
>
> **Security Headers**: The gateway automatically adds configurable security headers to all responses including CSP, X-Frame-Options, X-Content-Type-Options, X-Download-Options, and HSTS (on HTTPS). All headers can be individually enabled/disabled. Sensitive server headers are removed.
>
> **iframe Embedding**: By default, `X-Frame-Options: DENY` prevents iframe embedding for security. To allow embedding, set `X_FRAME_OPTIONS=SAMEORIGIN` (same domain) or disable with `X_FRAME_OPTIONS=""`. Also update CSP `frame-ancestors` directive if needed.
>
> **Cookie Security**: Authentication cookies are automatically configured with HttpOnly, Secure (in production), and SameSite attributes for CSRF protection.
>
> Note: do not quote the ALLOWED_ORIGINS values, this needs to be valid JSON, such as:
> ALLOWED_ORIGINS=["http://localhost", "http://localhost:4444"]
>
> Documentation endpoints (`/docs`, `/redoc`, `/openapi.json`) are always protected by authentication.
> By default, they require Bearer token authentication. Setting `DOCS_ALLOW_BASIC_AUTH=true` enables HTTP Basic Authentication as an additional method using the same credentials as `BASIC_AUTH_USER` and `BASIC_AUTH_PASSWORD`.


### Logging

MCP Gateway provides flexible logging with **stdout/stderr output by default** and **optional file-based logging**. When file logging is enabled, it provides JSON formatting for structured logs and text formatting for console output.

| Setting                 | Description                        | Default           | Options                    |
| ----------------------- | ---------------------------------- | ----------------- | -------------------------- |
| `LOG_LEVEL`             | Minimum log level                  | `INFO`            | `DEBUG`...`CRITICAL`       |
| `LOG_FORMAT`            | Console log format                 | `json`            | `json`, `text`             |
| `LOG_TO_FILE`           | **Enable file logging**            | **`false`**       | **`true`, `false`**        |
| `LOG_FILE`              | Log filename (when enabled)        | `null`            | `mcpgateway.log`           |
| `LOG_FOLDER`            | Directory for log files            | `null`            | `logs`, `/var/log/gateway` |
| `LOG_FILEMODE`          | File write mode                    | `a+`              | `a+` (append), `w` (overwrite)|
| `LOG_ROTATION_ENABLED`  | **Enable log file rotation**       | **`false`**       | **`true`, `false`**        |
| `LOG_MAX_SIZE_MB`       | Max file size before rotation (MB) | `1`               | Any positive integer       |
| `LOG_BACKUP_COUNT`      | Number of backup files to keep     | `5`               | Any non-negative integer   |

**Logging Behavior:**
- **Default**: Logs only to **stdout/stderr** with human-readable text format
- **File Logging**: When `LOG_TO_FILE=true`, logs to **both** file (JSON format) and console (text format)
- **Log Rotation**: When `LOG_ROTATION_ENABLED=true`, files rotate at `LOG_MAX_SIZE_MB` with `LOG_BACKUP_COUNT` backup files (e.g., `.log.1`, `.log.2`)
- **Directory Creation**: Log folder is automatically created if it doesn't exist
- **Centralized Service**: All modules use the unified `LoggingService` for consistent formatting

**Example Configurations:**

```bash
# Default: stdout/stderr only (recommended for containers)
LOG_LEVEL=INFO
# No additional config needed - logs to stdout/stderr

# Optional: Enable file logging (no rotation)
LOG_TO_FILE=true
LOG_FOLDER=/var/log/mcpgateway
LOG_FILE=gateway.log
LOG_FILEMODE=a+

# Optional: Enable file logging with rotation
LOG_TO_FILE=true
LOG_ROTATION_ENABLED=true
LOG_MAX_SIZE_MB=10
LOG_BACKUP_COUNT=3
LOG_FOLDER=/var/log/mcpgateway
LOG_FILE=gateway.log
```

**Default Behavior:**
- Logs are written **only to stdout/stderr** in human-readable text format
- File logging is **disabled by default** (no files created)
- Set `LOG_TO_FILE=true` to enable optional file logging with JSON format

### Observability (OpenTelemetry)

MCP Gateway includes **vendor-agnostic OpenTelemetry support** for distributed tracing. Works with Phoenix, Jaeger, Zipkin, Tempo, DataDog, New Relic, and any OTLP-compatible backend.

| Setting                         | Description                                    | Default               | Options                                    |
| ------------------------------- | ---------------------------------------------- | --------------------- | ------------------------------------------ |
| `OTEL_ENABLE_OBSERVABILITY`     | Master switch for observability               | `true`                | `true`, `false`                           |
| `OTEL_SERVICE_NAME`             | Service identifier in traces                   | `mcp-gateway`         | string                                     |
| `OTEL_SERVICE_VERSION`          | Service version in traces                      | `0.6.0`               | string                                     |
| `OTEL_DEPLOYMENT_ENVIRONMENT`   | Environment tag (dev/staging/prod)            | `development`         | string                                     |
| `OTEL_TRACES_EXPORTER`          | Trace exporter backend                         | `otlp`                | `otlp`, `jaeger`, `zipkin`, `console`, `none` |
| `OTEL_RESOURCE_ATTRIBUTES`      | Custom resource attributes                     | (empty)               | `key=value,key2=value2`                   |

**OTLP Configuration** (for Phoenix, Tempo, DataDog, etc.):

| Setting                         | Description                                    | Default               | Options                                    |
| ------------------------------- | ---------------------------------------------- | --------------------- | ------------------------------------------ |
| `OTEL_EXPORTER_OTLP_ENDPOINT`   | OTLP collector endpoint                        | (none)                | `http://localhost:4317`                   |
| `OTEL_EXPORTER_OTLP_PROTOCOL`   | OTLP protocol                                  | `grpc`                | `grpc`, `http/protobuf`                   |
| `OTEL_EXPORTER_OTLP_HEADERS`    | Authentication headers                         | (empty)               | `api-key=secret,x-auth=token`             |
| `OTEL_EXPORTER_OTLP_INSECURE`   | Skip TLS verification                          | `true`                | `true`, `false`                           |

**Alternative Backends** (optional):

| Setting                         | Description                                    | Default               | Options                                    |
| ------------------------------- | ---------------------------------------------- | --------------------- | ------------------------------------------ |
| `OTEL_EXPORTER_JAEGER_ENDPOINT` | Jaeger collector endpoint                      | `http://localhost:14268/api/traces` | URL                             |
| `OTEL_EXPORTER_ZIPKIN_ENDPOINT` | Zipkin collector endpoint                      | `http://localhost:9411/api/v2/spans` | URL                            |

**Performance Tuning**:

| Setting                         | Description                                    | Default               | Options                                    |
| ------------------------------- | ---------------------------------------------- | --------------------- | ------------------------------------------ |
| `OTEL_TRACES_SAMPLER`           | Sampling strategy                              | `parentbased_traceidratio` | `always_on`, `always_off`, `traceidratio` |
| `OTEL_TRACES_SAMPLER_ARG`       | Sample rate (0.0-1.0)                         | `0.1`                 | float (0.1 = 10% sampling)                |
| `OTEL_BSP_MAX_QUEUE_SIZE`       | Max queued spans                              | `2048`                | int > 0                                    |
| `OTEL_BSP_MAX_EXPORT_BATCH_SIZE`| Max batch size for export                     | `512`                 | int > 0                                    |
| `OTEL_BSP_SCHEDULE_DELAY`       | Export interval (ms)                          | `5000`                | int > 0                                    |

**Quick Start with Phoenix**:
```bash
# Start Phoenix for LLM observability
docker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latest

# Configure gateway
export OTEL_ENABLE_OBSERVABILITY=true
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

# Run gateway - traces automatically sent to Phoenix
mcpgateway
```

> ๐Ÿ” **What Gets Traced**: Tool invocations, prompt rendering, resource fetching, gateway federation, health checks, plugin execution (if enabled)
>
> ๐Ÿš€ **Zero Overhead**: When `OTEL_ENABLE_OBSERVABILITY=false`, all tracing is disabled with no performance impact
>
> ๐Ÿ“Š **View Traces**: Phoenix UI at `http://localhost:6006`, Jaeger at `http://localhost:16686`, or your configured backend

### 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                         |
| `SSE_KEEPALIVE_ENABLED`   | Enable SSE keepalive events        | `true`  | bool                            |
| `SSE_KEEPALIVE_INTERVAL`  | SSE keepalive interval (secs)      | `30`    | int > 0                         |
| `USE_STATEFUL_SESSIONS`   | streamable http config             | `false` | bool                            |
| `JSON_RESPONSE_ENABLED`   | json/sse streams (streamable http) | `true`  | bool                            |

> **๐Ÿ’ก SSE Keepalive Events**: The gateway sends periodic keepalive events to prevent connection timeouts with proxies and load balancers. Disable with `SSE_KEEPALIVE_ENABLED=false` if your client doesn't handle unknown event types. Common intervals: 30s (default), 60s (AWS ALB), 240s (Azure).

### 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.

### Database Management

MCP Gateway uses Alembic for database migrations. Common commands:

- `make db-current` - Show current database version
- `make db-upgrade` - Apply pending migrations
- `make db-migrate` - Create new migration
- `make db-history` - Show migration history
- `make db-status` - Detailed migration status

#### Troubleshooting

**Common Issues:**

- **"No 'script_location' key found"**: Ensure you're running from the project root directory.

- **"Unknown SSE event: keepalive" warnings**: Some MCP clients don't recognize keepalive events. These warnings are harmless and don't affect functionality. To disable: `SSE_KEEPALIVE_ENABLED=false`

- **Connection timeouts with proxies/load balancers**: If experiencing timeouts, adjust keepalive interval to match your infrastructure: `SSE_KEEPALIVE_INTERVAL=60` (AWS ALB) or `240` (Azure).

### 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>๐Ÿค– A2A Agent Management /a2a</strong></summary>

```bash
# Register a new A2A agent
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
           "name":"hello_world_agent",
           "endpoint_url":"http://localhost:9999/",
           "agent_type":"jsonrpc",
           "description":"External AI agent for hello world functionality",
           "auth_type":"api_key",
           "auth_value":"your-api-key",
           "tags":["ai", "hello-world"]
         }' \
     http://localhost:4444/a2a

# List A2A agents
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/a2a

# Get agent by ID
curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/a2a/agent-id

# Update agent
curl -X PUT -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{ "description":"Updated description" }' \
     http://localhost:4444/a2a/agent-id

# Test agent (direct invocation)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
           "parameters": {
             "method": "message/send",
             "params": {
               "message": {
                 "messageId": "test-123",
                 "role": "user",
                 "parts": [{"type": "text", "text": "Hello!"}]
               }
             }
           },
           "interaction_type": "test"
         }' \
     http://localhost:4444/a2a/agent-name/invoke

# Toggle agent status
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
     http://localhost:4444/a2a/agent-id/toggle?activate=false

# Delete agent
curl -X DELETE -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
     http://localhost:4444/a2a/agent-id

# Associate agent with virtual server (agents become available as MCP tools)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
           "name":"AI Assistant Server",
           "description":"Virtual server with AI agents",
           "associated_a2a_agents":["agent-id"]
         }' \
     http://localhost:4444/servers
```

> ๐Ÿค– **A2A Integration**: A2A agents are external AI agents that can be registered and exposed as MCP tools
> ๐Ÿ”„ **Protocol Detection**: Gateway automatically detects JSONRPC vs custom A2A protocols
> ๐Ÿ“Š **Testing**: Built-in test functionality via Admin UI or `/a2a/{agent_id}/test` endpoint
> ๐ŸŽ›๏ธ **Virtual Servers**: Associate agents with servers to expose them as standard MCP tools

</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
```

## Doctest Coverage

MCP Context Forge implements comprehensive doctest coverage to ensure all code examples in documentation are tested and verified:

```bash
make doctest         # Run all doctests
make doctest-verbose # Run with detailed output
make doctest-coverage # Generate coverage report
make doctest-check   # Check coverage percentage
```

**Coverage Status:**
- โœ… **Transport Modules**: 100% (base, stdio, SSE, WebSocket, streamable HTTP)
- โœ… **Utility Functions**: 100% (slug generation, JWT tokens, validation)
- โœ… **Configuration**: 100% (settings, environment variables)
- ๐Ÿ”„ **Service Classes**: ~60% (in progress)
- ๐Ÿ”„ **Complex Classes**: ~40% (in progress)

**Benefits:**
- All documented examples are automatically tested
- Documentation stays accurate and up-to-date
- Developers can run examples directly from docstrings
- Regression prevention through automated verification

For detailed information, see the [Doctest Coverage Guide](https://ibm.github.io/mcp-context-forge/development/doctest-coverage/).

---

## 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 containerized Sonar Scanner CLI with Docker
sonar-submit-podman  - Run containerized Sonar Scanner CLI with Podman
pysonar-scanner      - Run scan with Python wrapper (pysonar-scanner)
sonar-info           - How to create a token & which env vars to export
๐Ÿ›ก๏ธ SECURITY & PACKAGE SCANNING
trivy                - Scan container image for CVEs (HIGH/CRIT). Needs podman socket enabled
grype-scan           - Scan container for security audit and vulnerability scanning
dockle               - Lint the built container image via tarball (no daemon/socket needed)
hadolint             - Lint Containerfile/Dockerfile(s) with hadolint
pip-audit            - Audit Python dependencies for published CVEs
๐Ÿ“ฆ DEPENDENCY MANAGEMENT
deps-update          - Run update-deps.py to update all dependencies in pyproject.toml and docs/requirements.txt
containerfile-update - Update base image in Containerfile to latest tag
๐Ÿ“ฆ PACKAGING & PUBLISHING
dist                 - Clean-build wheel *and* sdist into ./dist
wheel                - Build wheel only
sdist                - Build source distribution only
verify               - Build + twine + check-manifest + pyroma (no upload)
publish              - Verify, then upload to PyPI (needs TWINE_* creds)
๐Ÿฆญ PODMAN CONTAINER BUILD & RUN
podman-dev           - Build development container image
podman               - Build container image
podman-prod          - Build production container image (using ubi-micro โ†’ scratch). Not supported on macOS.
podman-run           - Run the container on HTTP  (port 4444)
podman-run-shell     - Run the container on HTTP  (port 4444) and start a shell
podman-run-ssl       - Run the container on HTTPS (port 4444, self-signed)
podman-run-ssl-host  - Run the container on HTTPS with --network=host (port 4444, self-signed)
podman-stop          - Stop & remove the container
podman-test          - Quick curl smoke-test against the container
podman-logs          - Follow container logs (โŒƒC to quit)
podman-stats         - Show container resource stats (if supported)
podman-top           - Show live top-level process info in container
podman-shell         - Open an interactive shell inside the Podman container
๐Ÿ‹ DOCKER BUILD & RUN
docker-dev           - Build development Docker image
docker               - Build production Docker image
docker-prod          - Build production container image (using ubi-micro โ†’ scratch). Not supported on macOS.
docker-run           - Run the container on HTTP  (port 4444)
docker-run-ssl       - Run the container on HTTPS (port 4444, self-signed)
docker-stop          - Stop & remove the container
docker-test          - Quick curl smoke-test against the container
docker-logs          - Follow container logs (โŒƒC to quit)
docker-stats         - Show container resource usage stats (non-streaming)
docker-top           - Show top-level process info in Docker container
docker-shell         - Open an interactive shell inside the Docker container
๐Ÿ› ๏ธ COMPOSE STACK     - Build / start / stop the multi-service stack
compose-up           - Bring the whole stack up (detached)
compose-restart      - Recreate changed containers, pulling / building as needed
compose-build        - Build (or rebuild) images defined in the compose file
compose-pull         - Pull the latest images only
compose-logs         - Tail logs from all services (Ctrl-C to exit)
compose-ps           - Show container status table
compose-shell        - Open an interactive shell in the "gateway" container
compose-stop         - Gracefully stop the stack (keep containers)
compose-down         - Stop & remove containers (keep named volumes)
compose-rm           - Remove *stopped* containers
compose-clean        - โœจ Down **and** delete named volumes (data-loss โš )
โ˜๏ธ IBM CLOUD CODE ENGINE
ibmcloud-check-env          - Verify all required IBM Cloud env vars are set
ibmcloud-cli-install        - Auto-install IBM Cloud CLI + required plugins (OS auto-detected)
ibmcloud-login              - Login to IBM Cloud CLI using IBMCLOUD_API_KEY (--sso)
ibmcloud-ce-login           - Set Code Engine target project and region
ibmcloud-list-containers    - List deployed Code Engine apps
ibmcloud-tag                - Tag container image for IBM Container Registry
ibmcloud-push               - Push image to IBM Container Registry
ibmcloud-deploy             - Deploy (or update) container image in Code Engine
ibmcloud-ce-logs            - Stream logs for the deployed application
ibmcloud-ce-status          - Get deployment status
ibmcloud-ce-rm              - Delete the Code Engine application
๐Ÿงช MINIKUBE LOCAL CLUSTER
minikube-install      - Install Minikube (macOS, Linux, or Windows via choco)
helm-install          - Install Helm CLI (macOS, Linux, or Windows)
minikube-start        - Start local Minikube cluster with Ingress + DNS + metrics-server
minikube-stop         - Stop the Minikube cluster
minikube-delete       - Delete the Minikube cluster
minikube-image-load   - Build and load ghcr.io/ibm/mcp-context-forge:latest into Minikube
minikube-k8s-apply    - Apply Kubernetes manifests from deployment/k8s/
minikube-status       - Show status of Minikube and ingress pods
๐Ÿ› ๏ธ HELM CHART TASKS
helm-lint            - Lint the Helm chart (static analysis)
helm-package         - Package the chart into dist/ as mcp-stack-<ver>.tgz
helm-deploy          - Upgrade/Install chart into Minikube (profile mcpgw)
helm-delete          - Uninstall the chart release from Minikube
๐Ÿ  LOCAL PYPI SERVER
local-pypi-install   - Install pypiserver for local testing
local-pypi-start     - Start local PyPI server on :8084 (no auth)
local-pypi-start-auth - Start local PyPI server with basic auth (admin/admin)
local-pypi-stop      - Stop local PyPI server
local-pypi-upload    - Upload existing package to local PyPI (no auth)
local-pypi-upload-auth - Upload existing package to local PyPI (with auth)
local-pypi-test      - Install package from local PyPI
local-pypi-clean     - Full cycle: build โ†’ upload โ†’ install locally
๐Ÿ  LOCAL DEVPI SERVER
devpi-install        - Install devpi server and client
devpi-init           - Initialize devpi server (first time only)
devpi-start          - Start devpi server
devpi-stop           - Stop devpi server
devpi-setup-user     - Create user and dev index
devpi-upload         - Upload existing package to devpi
devpi-test           - Install package from devpi
devpi-clean          - Full cycle: build โ†’ upload โ†’ install locally
devpi-status         - Show devpi server status
devpi-web            - Open devpi web interface
```
</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

[![Star History Chart](https://api.star-history.com/svg?repos=ibm/mcp-context-forge&type=Date)](https://www.star-history.com/#ibm/mcp-context-forge&Date)

<!-- === Usage Stats === -->
[![PyPi Downloads](https://static.pepy.tech/badge/mcp-contextforge-gateway/month)](https://pepy.tech/project/mcp-contextforge-gateway)&nbsp;
[![Stars](https://img.shields.io/github/stars/ibm/mcp-context-forge?style=social)](https://github.com/ibm/mcp-context-forge/stargazers)&nbsp;
[![Forks](https://img.shields.io/github/forks/ibm/mcp-context-forge?style=social)](https://github.com/ibm/mcp-context-forge/network/members)&nbsp;
[![Contributors](https://img.shields.io/github/contributors/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/graphs/contributors)&nbsp;
[![Last Commit](https://img.shields.io/github/last-commit/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/commits)&nbsp;
[![Open Issues](https://img.shields.io/github/issues/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/issues)&nbsp;

            

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/6d/33/732900b528194447f4f640d21b68a68758dc88e14a49627afa6d5bd477c9/mcp_contextforge_gateway-0.6.0.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![](docs/docs/images/contextforge-banner.png)\n\n<!-- === CI / Security / Build Badges === -->\n[![Build Python Package](https://github.com/IBM/mcp-context-forge/actions/workflows/python-package.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/python-package.yml)&nbsp;\n[![CodeQL](https://github.com/IBM/mcp-context-forge/actions/workflows/codeql.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/codeql.yml)&nbsp;\n[![Bandit Security](https://github.com/IBM/mcp-context-forge/actions/workflows/bandit.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/bandit.yml)&nbsp;\n[![Dependency Review](https://github.com/IBM/mcp-context-forge/actions/workflows/dependency-review.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/dependency-review.yml)&nbsp;\n[![Tests & Coverage](https://github.com/IBM/mcp-context-forge/actions/workflows/pytest.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/pytest.yml)&nbsp;\n[![Lint & Static Analysis](https://github.com/IBM/mcp-context-forge/actions/workflows/lint.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/lint.yml)\n\n<!-- === Container Build & Deploy === -->\n[![Secure Docker Build](https://github.com/IBM/mcp-context-forge/actions/workflows/docker-image.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/docker-image.yml)&nbsp;\n[![Deploy to IBM Code Engine](https://github.com/IBM/mcp-context-forge/actions/workflows/ibm-cloud-code-engine.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/ibm-cloud-code-engine.yml)\n\n<!-- === Package / Container === -->\n[![Async](https://img.shields.io/badge/async-await-green.svg)](https://docs.python.org/3/library/asyncio.html)\n[![License](https://img.shields.io/github/license/ibm/mcp-context-forge)](LICENSE)&nbsp;\n[![PyPI](https://img.shields.io/pypi/v/mcp-contextforge-gateway)](https://pypi.org/project/mcp-contextforge-gateway/)&nbsp;\n[![Docker Image](https://img.shields.io/badge/docker-ghcr.io%2Fibm%2Fmcp--context--forge-blue)](https://github.com/ibm/mcp-context-forge/pkgs/container/mcp-context-forge)&nbsp;\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![MCP Gateway](https://ibm.github.io/mcp-context-forge/images/mcpgateway.gif)\n---\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. [Containerized (self-signed TLS)](#containerized-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 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\n**\u26a0\ufe0f Caution**: The current release (0.6.0) is considered alpha / early beta. It is not production-ready and should only be used for local development, testing, or experimentation. Features, APIs, and behaviors are subject to change without notice. **Do not** deploy in production environments without thorough security review, validation and additional security mechanisms.  Many of the features required for secure, large-scale, or multi-tenant production deployments are still on the [project roadmap](https://ibm.github.io/mcp-context-forge/architecture/roadmap/) - which is itself evolving.\n\nIt currently supports:\n\n* Federation across multiple MCP and REST services\n* **A2A (Agent-to-Agent) integration** for external AI agents (OpenAI, Anthropic, custom)\n* Virtualization of legacy APIs as MCP-compliant tools and servers\n* Transport over HTTP, JSON-RPC, WebSocket, SSE (with configurable keepalive), stdio and streamable-HTTP\n* An Admin UI for real-time management, configuration, and log monitoring\n* Built-in auth, retries, and rate-limiting\n* **OpenTelemetry observability** with Phoenix, Jaeger, Zipkin, and other OTLP backends\n* Scalable deployments via Docker or PyPI, Redis-backed caching, and multi-cluster federation\n\n![MCP Gateway Architecture](https://ibm.github.io/mcp-context-forge/images/mcpgateway.svg)\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**\u26a0\ufe0f Important**: MCP Gateway is not a standalone product - it is an open source component with **NO OFFICIAL SUPPORT** from IBM or its affiliates that can be integrated into your own solution architecture. If you choose to use it, you are responsible for evaluating its fit, securing the deployment, and managing its lifecycle. See [SECURITY.md](./SECURITY.md) for more details.\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* Real-time log viewer with filtering, search, and export capabilities\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<details>\n<summary><strong>\ud83d\udd0d OpenTelemetry Observability</strong></summary>\n\n* **Vendor-agnostic tracing** with OpenTelemetry (OTLP) protocol support\n* **Multiple backend support**: Phoenix (LLM-focused), Jaeger, Zipkin, Tempo, DataDog, New Relic\n* **Distributed tracing** across federated gateways and services\n* **Automatic instrumentation** of tools, prompts, resources, and gateway operations\n* **LLM-specific metrics**: Token usage, costs, model performance\n* **Zero-overhead when disabled** with graceful degradation\n* **Easy configuration** via environment variables\n\nQuick start with Phoenix (LLM observability):\n```bash\n# Start Phoenix\ndocker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latest\n\n# Configure gateway\nexport OTEL_ENABLE_OBSERVABILITY=true\nexport OTEL_TRACES_EXPORTER=otlp\nexport OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317\n\n# Run gateway - traces automatically sent to Phoenix\nmcpgateway\n```\n\nSee [Observability Documentation](https://ibm.github.io/mcp-context-forge/manage/observability/) for detailed setup with other backends.\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**TLDR;**:\n(single command using [uv](https://docs.astral.sh/uv/))\n\n```bash\nBASIC_AUTH_PASSWORD=pass \\\nMCPGATEWAY_UI_ENABLED=true \\\nMCPGATEWAY_ADMIN_API_ENABLED=true \\\nuvx --from mcp-contextforge-gateway mcpgateway --host 0.0.0.0 --port 4444\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>Windows (PowerShell) quick-start</strong></summary>\n\n```powershell\n# 1\ufe0f\u20e3  Isolated env + install from PyPI\nmkdir mcpgateway ; cd mcpgateway\npython3 -m venv .venv ; .\\.venv\\Scripts\\Activate.ps1\npip install --upgrade pip\npip install mcp-contextforge-gateway\n\n# 2\ufe0f\u20e3  Environment variables (session-only)\n$Env:MCPGATEWAY_UI_ENABLED        = \"true\"\n$Env:MCPGATEWAY_ADMIN_API_ENABLED = \"true\"\n$Env:BASIC_AUTH_PASSWORD          = \"changeme\"      # admin/changeme\n$Env:JWT_SECRET_KEY               = \"my-test-key\"\n\n# 3\ufe0f\u20e3  Launch the gateway\nmcpgateway.exe --host 0.0.0.0 --port 4444\n\n#   Optional: background it\n# Start-Process -FilePath \"mcpgateway.exe\" -ArgumentList \"--host 0.0.0.0 --port 4444\"\n\n# 4\ufe0f\u20e3  Bearer token and smoke-test\n$Env: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 $Env:MCPGATEWAY_BEARER_TOKEN\" `\n     http://127.0.0.1:4444/version | jq\n```\n\n</details>\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     --expose-sse \\\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\" --expose-sse --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\" --expose-sse --port 8002\n\n# NEW: Expose via multiple protocols simultaneously!\npython3 -m mcpgateway.translate \\\n     --stdio \"uvx mcp-server-git\" \\\n     --expose-sse \\\n     --expose-streamable-http \\\n     --port 9000\n# Now accessible via both /sse (SSE) and /mcp (streamable HTTP) endpoints\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=$MCPGATEWAY_BEARER_TOKEN\nexport MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp\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` and `MCP_SERVER_URL` 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_URL='http://localhost:4444/servers/UUID_OF_SERVER_1/mcp'\nexport MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN}\nnpx -y @modelcontextprotocol/inspector\n```\n\nor\n\nPass the url and auth as arguments (no need to set environment variables)\n```bash\nnpx -y @modelcontextprotocol/inspector\ncommand as `python`\nArguments as `-m mcpgateway.wrapper --url \"http://localhost:4444/servers/UUID_OF_SERVER_1/mcp\" --auth \"Bearer <your token>\"`\n```\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\": \"your-token-here\",\n        \"MCP_SERVER_URL\": \"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 MCPGATEWAY_UI_ENABLED=true \\\n  -e MCPGATEWAY_ADMIN_API_ENABLED=true \\\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.6.0\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.6.0 \\\n  python3 -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 MCPGATEWAY_UI_ENABLED=true \\\n  -e MCPGATEWAY_ADMIN_API_ENABLED=true \\\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.6.0\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 MCPGATEWAY_UI_ENABLED=true \\\n  -e MCPGATEWAY_ADMIN_API_ENABLED=true \\\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.6.0\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.6.0\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.6.0\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.6.0\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.6.0`) 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=${MCPGATEWAY_BEARER_TOKEN}\nexport MCP_SERVER_URL='http://localhost:4444/servers/UUID_OF_SERVER_1/mcp'\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=$MCPGATEWAY_BEARER_TOKEN \\\n  -e MCP_SERVER_URL=http://host.docker.internal:4444/servers/UUID_OF_SERVER_1/mcp \\\n  -e MCP_TOOL_CALL_TIMEOUT=120 \\\n  -e MCP_WRAPPER_LOG_LEVEL=DEBUG \\\n  ghcr.io/ibm/mcp-context-forge:0.6.0 \\\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=${MCPGATEWAY_BEARER_TOKEN}\nexport MCP_SERVER_URL=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.6.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/mcp`.\n> When inside Docker/Podman, that often becomes `http://host.docker.internal:4444/servers/UUID_OF_SERVER_1/mcp` (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_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \\\n  -e MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN} \\\n  -e MCP_TOOL_CALL_TIMEOUT=120 \\\n  ghcr.io/ibm/mcp-context-forge:0.6.0 \\\n  python3 -m mcpgateway.wrapper\n```\n\n</details>\n\n---\n\n<details>\n<summary><strong>\ud83d\udce6 pipx (one-liner install &amp; 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=${MCPGATEWAY_BEARER_TOKEN} \\\nMCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \\\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\": \"<your-token>\",\n        \"MCP_SERVER_URL\": \"http://localhost:4444/servers/UUID_OF_SERVER_1/mcp\",\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=${MCPGATEWAY_BEARER_TOKEN} \\\nMCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \\\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\": \"<your-token>\",\n        \"MCP_SERVER_URL\": \"http://localhost:4444/servers/UUID_OF_SERVER_1/mcp\"\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 containerized 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### Containerized (self-signed TLS)\n\n## Container Runtime Support\n\nThis project supports both Docker and Podman. The Makefile automatically detects\nwhich runtime is available and handles image naming differences.\n\n### Auto-detection\n```bash\nmake container-build  # Uses podman if available, otherwise docker\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                 | `127.0.0.1`            | 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 | `false` | bool    |\n| `MCPGATEWAY_ADMIN_API_ENABLED` | Enable API endpoints for admin ops     | `false` | bool    |\n| `MCPGATEWAY_BULK_IMPORT_ENABLED` | Enable bulk import endpoint for tools | `true`  | bool    |\n\n> \ud83d\udda5\ufe0f Set both UI and Admin API to `false` to disable management UI and APIs in production.\n> \ud83d\udce5 The bulk import endpoint allows importing up to 200 tools in a single request via `/admin/tools/import`.\n\n### A2A (Agent-to-Agent) Features\n\n| Setting                        | Description                            | Default | Options |\n| ------------------------------ | -------------------------------------- | ------- | ------- |\n| `MCPGATEWAY_A2A_ENABLED`       | Enable A2A agent features             | `true`  | bool    |\n| `MCPGATEWAY_A2A_MAX_AGENTS`    | Maximum number of A2A agents allowed  | `100`   | int     |\n| `MCPGATEWAY_A2A_DEFAULT_TIMEOUT` | Default timeout for A2A HTTP requests (seconds) | `30` | int |\n| `MCPGATEWAY_A2A_MAX_RETRIES`   | Maximum retry attempts for A2A calls  | `3`     | int     |\n| `MCPGATEWAY_A2A_METRICS_ENABLED` | Enable A2A agent metrics collection | `true`  | bool    |\n\n> \ud83e\udd16 **A2A Integration**: Register external AI agents (OpenAI, Anthropic, custom) and expose them as MCP tools\n> \ud83d\udcca **Metrics**: Track agent performance, success rates, and response times\n> \ud83d\udd12 **Security**: Encrypted credential storage and configurable authentication\n> \ud83c\udf9b\ufe0f **Admin UI**: Dedicated tab for agent management with test functionality\n\n**A2A Configuration Effects:**\n- `MCPGATEWAY_A2A_ENABLED=false`: Completely disables A2A features (API endpoints return 404, admin tab hidden)\n- `MCPGATEWAY_A2A_METRICS_ENABLED=false`: Disables metrics collection while keeping functionality\n\n### Security\n\n| Setting                   | Description                    | Default                                        | Options    |\n| ------------------------- | ------------------------------ | ---------------------------------------------- | ---------- |\n| `SKIP_SSL_VERIFY`         | Skip upstream TLS verification | `false`                                        | bool       |\n| `ENVIRONMENT`             | Deployment environment (affects security defaults) | `development`                              | `development`/`production` |\n| `APP_DOMAIN`              | Domain for production CORS origins | `localhost`                                 | string     |\n| `ALLOWED_ORIGINS`         | CORS allow-list                | Auto-configured by environment                 | JSON array |\n| `CORS_ENABLED`            | Enable CORS                    | `true`                                         | bool       |\n| `CORS_ALLOW_CREDENTIALS`  | Allow credentials in CORS      | `true`                                         | bool       |\n| `SECURE_COOKIES`          | Force secure cookie flags     | `true`                                         | bool       |\n| `COOKIE_SAMESITE`         | Cookie SameSite attribute      | `lax`                                          | `strict`/`lax`/`none` |\n| `SECURITY_HEADERS_ENABLED` | Enable security headers middleware | `true`                                     | bool       |\n| `X_FRAME_OPTIONS`         | X-Frame-Options header value   | `DENY`                                         | `DENY`/`SAMEORIGIN` |\n| `HSTS_ENABLED`            | Enable HSTS header             | `true`                                         | bool       |\n| `HSTS_MAX_AGE`            | HSTS max age in seconds        | `31536000`                                     | int        |\n| `REMOVE_SERVER_HEADERS`   | Remove server identification   | `true`                                         | bool       |\n| `DOCS_ALLOW_BASIC_AUTH`   | Allow Basic Auth for docs (in addition to JWT)         | `false`                                        | bool       |\n\n> **CORS Configuration**: When `ENVIRONMENT=development`, CORS origins are automatically configured for common development ports (3000, 8080, gateway port). In production, origins are constructed from `APP_DOMAIN` (e.g., `https://yourdomain.com`, `https://app.yourdomain.com`). You can override this by explicitly setting `ALLOWED_ORIGINS`.\n>\n> **Security Headers**: The gateway automatically adds configurable security headers to all responses including CSP, X-Frame-Options, X-Content-Type-Options, X-Download-Options, and HSTS (on HTTPS). All headers can be individually enabled/disabled. Sensitive server headers are removed.\n>\n> **iframe Embedding**: By default, `X-Frame-Options: DENY` prevents iframe embedding for security. To allow embedding, set `X_FRAME_OPTIONS=SAMEORIGIN` (same domain) or disable with `X_FRAME_OPTIONS=\"\"`. Also update CSP `frame-ancestors` directive if needed.\n>\n> **Cookie Security**: Authentication cookies are automatically configured with HttpOnly, Secure (in production), and SameSite attributes for CSRF protection.\n>\n> Note: do not quote the ALLOWED_ORIGINS values, this needs to be valid JSON, such as:\n> ALLOWED_ORIGINS=[\"http://localhost\", \"http://localhost:4444\"]\n>\n> Documentation endpoints (`/docs`, `/redoc`, `/openapi.json`) are always protected by authentication.\n> By default, they require Bearer token authentication. Setting `DOCS_ALLOW_BASIC_AUTH=true` enables HTTP Basic Authentication as an additional method using the same credentials as `BASIC_AUTH_USER` and `BASIC_AUTH_PASSWORD`.\n\n\n### Logging\n\nMCP Gateway provides flexible logging with **stdout/stderr output by default** and **optional file-based logging**. When file logging is enabled, it provides JSON formatting for structured logs and text formatting for console output.\n\n| Setting                 | Description                        | Default           | Options                    |\n| ----------------------- | ---------------------------------- | ----------------- | -------------------------- |\n| `LOG_LEVEL`             | Minimum log level                  | `INFO`            | `DEBUG`...`CRITICAL`       |\n| `LOG_FORMAT`            | Console log format                 | `json`            | `json`, `text`             |\n| `LOG_TO_FILE`           | **Enable file logging**            | **`false`**       | **`true`, `false`**        |\n| `LOG_FILE`              | Log filename (when enabled)        | `null`            | `mcpgateway.log`           |\n| `LOG_FOLDER`            | Directory for log files            | `null`            | `logs`, `/var/log/gateway` |\n| `LOG_FILEMODE`          | File write mode                    | `a+`              | `a+` (append), `w` (overwrite)|\n| `LOG_ROTATION_ENABLED`  | **Enable log file rotation**       | **`false`**       | **`true`, `false`**        |\n| `LOG_MAX_SIZE_MB`       | Max file size before rotation (MB) | `1`               | Any positive integer       |\n| `LOG_BACKUP_COUNT`      | Number of backup files to keep     | `5`               | Any non-negative integer   |\n\n**Logging Behavior:**\n- **Default**: Logs only to **stdout/stderr** with human-readable text format\n- **File Logging**: When `LOG_TO_FILE=true`, logs to **both** file (JSON format) and console (text format)\n- **Log Rotation**: When `LOG_ROTATION_ENABLED=true`, files rotate at `LOG_MAX_SIZE_MB` with `LOG_BACKUP_COUNT` backup files (e.g., `.log.1`, `.log.2`)\n- **Directory Creation**: Log folder is automatically created if it doesn't exist\n- **Centralized Service**: All modules use the unified `LoggingService` for consistent formatting\n\n**Example Configurations:**\n\n```bash\n# Default: stdout/stderr only (recommended for containers)\nLOG_LEVEL=INFO\n# No additional config needed - logs to stdout/stderr\n\n# Optional: Enable file logging (no rotation)\nLOG_TO_FILE=true\nLOG_FOLDER=/var/log/mcpgateway\nLOG_FILE=gateway.log\nLOG_FILEMODE=a+\n\n# Optional: Enable file logging with rotation\nLOG_TO_FILE=true\nLOG_ROTATION_ENABLED=true\nLOG_MAX_SIZE_MB=10\nLOG_BACKUP_COUNT=3\nLOG_FOLDER=/var/log/mcpgateway\nLOG_FILE=gateway.log\n```\n\n**Default Behavior:**\n- Logs are written **only to stdout/stderr** in human-readable text format\n- File logging is **disabled by default** (no files created)\n- Set `LOG_TO_FILE=true` to enable optional file logging with JSON format\n\n### Observability (OpenTelemetry)\n\nMCP Gateway includes **vendor-agnostic OpenTelemetry support** for distributed tracing. Works with Phoenix, Jaeger, Zipkin, Tempo, DataDog, New Relic, and any OTLP-compatible backend.\n\n| Setting                         | Description                                    | Default               | Options                                    |\n| ------------------------------- | ---------------------------------------------- | --------------------- | ------------------------------------------ |\n| `OTEL_ENABLE_OBSERVABILITY`     | Master switch for observability               | `true`                | `true`, `false`                           |\n| `OTEL_SERVICE_NAME`             | Service identifier in traces                   | `mcp-gateway`         | string                                     |\n| `OTEL_SERVICE_VERSION`          | Service version in traces                      | `0.6.0`               | string                                     |\n| `OTEL_DEPLOYMENT_ENVIRONMENT`   | Environment tag (dev/staging/prod)            | `development`         | string                                     |\n| `OTEL_TRACES_EXPORTER`          | Trace exporter backend                         | `otlp`                | `otlp`, `jaeger`, `zipkin`, `console`, `none` |\n| `OTEL_RESOURCE_ATTRIBUTES`      | Custom resource attributes                     | (empty)               | `key=value,key2=value2`                   |\n\n**OTLP Configuration** (for Phoenix, Tempo, DataDog, etc.):\n\n| Setting                         | Description                                    | Default               | Options                                    |\n| ------------------------------- | ---------------------------------------------- | --------------------- | ------------------------------------------ |\n| `OTEL_EXPORTER_OTLP_ENDPOINT`   | OTLP collector endpoint                        | (none)                | `http://localhost:4317`                   |\n| `OTEL_EXPORTER_OTLP_PROTOCOL`   | OTLP protocol                                  | `grpc`                | `grpc`, `http/protobuf`                   |\n| `OTEL_EXPORTER_OTLP_HEADERS`    | Authentication headers                         | (empty)               | `api-key=secret,x-auth=token`             |\n| `OTEL_EXPORTER_OTLP_INSECURE`   | Skip TLS verification                          | `true`                | `true`, `false`                           |\n\n**Alternative Backends** (optional):\n\n| Setting                         | Description                                    | Default               | Options                                    |\n| ------------------------------- | ---------------------------------------------- | --------------------- | ------------------------------------------ |\n| `OTEL_EXPORTER_JAEGER_ENDPOINT` | Jaeger collector endpoint                      | `http://localhost:14268/api/traces` | URL                             |\n| `OTEL_EXPORTER_ZIPKIN_ENDPOINT` | Zipkin collector endpoint                      | `http://localhost:9411/api/v2/spans` | URL                            |\n\n**Performance Tuning**:\n\n| Setting                         | Description                                    | Default               | Options                                    |\n| ------------------------------- | ---------------------------------------------- | --------------------- | ------------------------------------------ |\n| `OTEL_TRACES_SAMPLER`           | Sampling strategy                              | `parentbased_traceidratio` | `always_on`, `always_off`, `traceidratio` |\n| `OTEL_TRACES_SAMPLER_ARG`       | Sample rate (0.0-1.0)                         | `0.1`                 | float (0.1 = 10% sampling)                |\n| `OTEL_BSP_MAX_QUEUE_SIZE`       | Max queued spans                              | `2048`                | int > 0                                    |\n| `OTEL_BSP_MAX_EXPORT_BATCH_SIZE`| Max batch size for export                     | `512`                 | int > 0                                    |\n| `OTEL_BSP_SCHEDULE_DELAY`       | Export interval (ms)                          | `5000`                | int > 0                                    |\n\n**Quick Start with Phoenix**:\n```bash\n# Start Phoenix for LLM observability\ndocker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latest\n\n# Configure gateway\nexport OTEL_ENABLE_OBSERVABILITY=true\nexport OTEL_TRACES_EXPORTER=otlp\nexport OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317\n\n# Run gateway - traces automatically sent to Phoenix\nmcpgateway\n```\n\n> \ud83d\udd0d **What Gets Traced**: Tool invocations, prompt rendering, resource fetching, gateway federation, health checks, plugin execution (if enabled)\n>\n> \ud83d\ude80 **Zero Overhead**: When `OTEL_ENABLE_OBSERVABILITY=false`, all tracing is disabled with no performance impact\n>\n> \ud83d\udcca **View Traces**: Phoenix UI at `http://localhost:6006`, Jaeger at `http://localhost:16686`, or your configured backend\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| `SSE_KEEPALIVE_ENABLED`   | Enable SSE keepalive events        | `true`  | bool                            |\n| `SSE_KEEPALIVE_INTERVAL`  | SSE keepalive interval (secs)      | `30`    | int > 0                         |\n| `USE_STATEFUL_SESSIONS`   | streamable http config             | `false` | bool                            |\n| `JSON_RESPONSE_ENABLED`   | json/sse streams (streamable http) | `true`  | bool                            |\n\n> **\ud83d\udca1 SSE Keepalive Events**: The gateway sends periodic keepalive events to prevent connection timeouts with proxies and load balancers. Disable with `SSE_KEEPALIVE_ENABLED=false` if your client doesn't handle unknown event types. Common intervals: 30s (default), 60s (AWS ALB), 240s (Azure).\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### Database Management\n\nMCP Gateway uses Alembic for database migrations. Common commands:\n\n- `make db-current` - Show current database version\n- `make db-upgrade` - Apply pending migrations\n- `make db-migrate` - Create new migration\n- `make db-history` - Show migration history\n- `make db-status` - Detailed migration status\n\n#### Troubleshooting\n\n**Common Issues:**\n\n- **\"No 'script_location' key found\"**: Ensure you're running from the project root directory.\n\n- **\"Unknown SSE event: keepalive\" warnings**: Some MCP clients don't recognize keepalive events. These warnings are harmless and don't affect functionality. To disable: `SSE_KEEPALIVE_ENABLED=false`\n\n- **Connection timeouts with proxies/load balancers**: If experiencing timeouts, adjust keepalive interval to match your infrastructure: `SSE_KEEPALIVE_INTERVAL=60` (AWS ALB) or `240` (Azure).\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>\ud83e\udd16 A2A Agent Management /a2a</strong></summary>\n\n```bash\n# Register a new A2A agent\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n           \"name\":\"hello_world_agent\",\n           \"endpoint_url\":\"http://localhost:9999/\",\n           \"agent_type\":\"jsonrpc\",\n           \"description\":\"External AI agent for hello world functionality\",\n           \"auth_type\":\"api_key\",\n           \"auth_value\":\"your-api-key\",\n           \"tags\":[\"ai\", \"hello-world\"]\n         }' \\\n     http://localhost:4444/a2a\n\n# List A2A agents\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/a2a\n\n# Get agent by ID\ncurl -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" http://localhost:4444/a2a/agent-id\n\n# Update agent\ncurl -X PUT -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{ \"description\":\"Updated description\" }' \\\n     http://localhost:4444/a2a/agent-id\n\n# Test agent (direct invocation)\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n           \"parameters\": {\n             \"method\": \"message/send\",\n             \"params\": {\n               \"message\": {\n                 \"messageId\": \"test-123\",\n                 \"role\": \"user\",\n                 \"parts\": [{\"type\": \"text\", \"text\": \"Hello!\"}]\n               }\n             }\n           },\n           \"interaction_type\": \"test\"\n         }' \\\n     http://localhost:4444/a2a/agent-name/invoke\n\n# Toggle agent status\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n     http://localhost:4444/a2a/agent-id/toggle?activate=false\n\n# Delete agent\ncurl -X DELETE -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n     http://localhost:4444/a2a/agent-id\n\n# Associate agent with virtual server (agents become available as MCP tools)\ncurl -X POST -H \"Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\n           \"name\":\"AI Assistant Server\",\n           \"description\":\"Virtual server with AI agents\",\n           \"associated_a2a_agents\":[\"agent-id\"]\n         }' \\\n     http://localhost:4444/servers\n```\n\n> \ud83e\udd16 **A2A Integration**: A2A agents are external AI agents that can be registered and exposed as MCP tools\n> \ud83d\udd04 **Protocol Detection**: Gateway automatically detects JSONRPC vs custom A2A protocols\n> \ud83d\udcca **Testing**: Built-in test functionality via Admin UI or `/a2a/{agent_id}/test` endpoint\n> \ud83c\udf9b\ufe0f **Virtual Servers**: Associate agents with servers to expose them as standard MCP tools\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## Doctest Coverage\n\nMCP Context Forge implements comprehensive doctest coverage to ensure all code examples in documentation are tested and verified:\n\n```bash\nmake doctest         # Run all doctests\nmake doctest-verbose # Run with detailed output\nmake doctest-coverage # Generate coverage report\nmake doctest-check   # Check coverage percentage\n```\n\n**Coverage Status:**\n- \u2705 **Transport Modules**: 100% (base, stdio, SSE, WebSocket, streamable HTTP)\n- \u2705 **Utility Functions**: 100% (slug generation, JWT tokens, validation)\n- \u2705 **Configuration**: 100% (settings, environment variables)\n- \ud83d\udd04 **Service Classes**: ~60% (in progress)\n- \ud83d\udd04 **Complex Classes**: ~40% (in progress)\n\n**Benefits:**\n- All documented examples are automatically tested\n- Documentation stays accurate and up-to-date\n- Developers can run examples directly from docstrings\n- Regression prevention through automated verification\n\nFor detailed information, see the [Doctest Coverage Guide](https://ibm.github.io/mcp-context-forge/development/doctest-coverage/).\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 containerized Sonar Scanner CLI with Docker\nsonar-submit-podman  - Run containerized 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\ngrype-scan           - Scan container for security audit and vulnerability scanning\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 deployment/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[![Star History Chart](https://api.star-history.com/svg?repos=ibm/mcp-context-forge&type=Date)](https://www.star-history.com/#ibm/mcp-context-forge&Date)\n\n<!-- === Usage Stats === -->\n[![PyPi Downloads](https://static.pepy.tech/badge/mcp-contextforge-gateway/month)](https://pepy.tech/project/mcp-contextforge-gateway)&nbsp;\n[![Stars](https://img.shields.io/github/stars/ibm/mcp-context-forge?style=social)](https://github.com/ibm/mcp-context-forge/stargazers)&nbsp;\n[![Forks](https://img.shields.io/github/forks/ibm/mcp-context-forge?style=social)](https://github.com/ibm/mcp-context-forge/network/members)&nbsp;\n[![Contributors](https://img.shields.io/github/contributors/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/graphs/contributors)&nbsp;\n[![Last Commit](https://img.shields.io/github/last-commit/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/commits)&nbsp;\n[![Open Issues](https://img.shields.io/github/issues/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/issues)&nbsp;\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.6.0",
    "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": "48cb354bfd5821cdf6116166b099a0a5cfa4f7ac79d4f5508097fdcbf7dfa8c4",
                "md5": "b9de0e94e91751952c5d777bbc7145a9",
                "sha256": "cef1206a09e31963b5b5491db69fee130910fdab91928b6c5b030bd30f2ff736"
            },
            "downloads": -1,
            "filename": "mcp_contextforge_gateway-0.6.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b9de0e94e91751952c5d777bbc7145a9",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<3.14,>=3.11",
            "size": 580286,
            "upload_time": "2025-08-22T19:04:47",
            "upload_time_iso_8601": "2025-08-22T19:04:47.814699Z",
            "url": "https://files.pythonhosted.org/packages/48/cb/354bfd5821cdf6116166b099a0a5cfa4f7ac79d4f5508097fdcbf7dfa8c4/mcp_contextforge_gateway-0.6.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "6d33732900b528194447f4f640d21b68a68758dc88e14a49627afa6d5bd477c9",
                "md5": "9ee5c2a933bd1e5dca16efce7312030a",
                "sha256": "93c5ec67a8d4d3e13e56fd6f407fecd806504e7e57093c648414827c29dc9190"
            },
            "downloads": -1,
            "filename": "mcp_contextforge_gateway-0.6.0.tar.gz",
            "has_sig": false,
            "md5_digest": "9ee5c2a933bd1e5dca16efce7312030a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<3.14,>=3.11",
            "size": 928891,
            "upload_time": "2025-08-22T19:04:50",
            "upload_time_iso_8601": "2025-08-22T19:04:50.352444Z",
            "url": "https://files.pythonhosted.org/packages/6d/33/732900b528194447f4f640d21b68a68758dc88e14a49627afa6d5bd477c9/mcp_contextforge_gateway-0.6.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-22 19:04:50",
    "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"
}
        
Elapsed time: 0.81857s