Skip to content

Commit a41bf26

Browse files
authored
Merge pull request #93 from IBM/release-planning-cleanup-v2
Release planning cleanup v2
2 parents 6d184ad + 6153f6e commit a41bf26

File tree

3 files changed

+236
-48
lines changed

3 files changed

+236
-48
lines changed

README.md

Lines changed: 153 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,103 @@
11
# MCP Gateway
22

3-
[![CodeQL Advanced](https://github.com/IBM/mcp-context-forge/actions/workflows/codeql.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/codeql.yml) [![Bandit](https://github.com/IBM/mcp-context-forge/actions/workflows/bandit.yml/badge.svg)](https://github.com/IBM/mcp-context-forge/actions/workflows/bandit.yml) [![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) [![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) [![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) [![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) [![License](https://img.shields.io/github/license/ibm/mcp-context-forge)](LICENSE) [![PyPI](https://img.shields.io/pypi/v/mcp-contextforge-gateway)](https://pypi.org/project/mcp-contextforge-gateway/)
3+
> Model Context Protocol gateway & proxy - unify REST, MCP and A2A with federation, virtual servers, retries, security and an optional admin UI.
4+
# MCP Gateway
5+
6+
> Model Context Protocol gateway & proxy — unify REST, MCP, and A2A with federation, virtual servers, retries, security, and an optional admin UI.
7+
8+
<!-- === CI / Security / Build Badges === -->
9+
[![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;
10+
[![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;
11+
[![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;
12+
[![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;
13+
14+
<!-- === Container Build & Deploy === -->
15+
[![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;
16+
[![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)
17+
18+
<!-- === Package / Container === -->
19+
[![License](https://img.shields.io/github/license/ibm/mcp-context-forge)](LICENSE)&nbsp;
20+
[![PyPI](https://img.shields.io/pypi/v/mcp-contextforge-gateway)](https://pypi.org/project/mcp-contextforge-gateway/)&nbsp;
21+
[![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;
422

5-
A flexible feature-rich FastAPI-based gateway for the Model Context Protocol (MCP) that unifies and federates tools, resources, prompts, servers and peer gateways, wraps any REST API as MCP-compliant tools or virtual servers, and exposes everything over HTTP/JSON-RPC, WebSocket, Server-Sent Events (SSE) and stdio transports—all manageable via a rich, interactive Admin UI and packaged as a container with support for any SQLAlchemy supported database.
23+
24+
ContextForge MCP Gateway is a feature-rich gateway & proxy that federates MCP and REST services - unifying discovery, auth, rate-limiting, observability, virtual servers, multi-transport protocols, and an optional live 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.
625

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

10-
## Overview & Goals
29+
## 🚀 Overview & Goals
30+
31+
**ContextForge MCP Gateway** is a production-grade gateway, registry, and proxy that sits in front of any [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server or REST API—exposing a unified endpoint for all your AI clients.
1132

12-
MCP Gateway builds on the MCP spec by sitting **in front of** MCP Server or REST API to:
33+
It supports:
1334

14-
* **Act as a true gateway**, centralizing tool, resource and prompt registries while preserving the official MCP 2025-03-26 protocol
15-
* **Federate** multiple MCP servers into one unified endpoint—auto-discover peers (mDNS or explicit), health-check them, and merge their capabilities
16-
* **Virtualize** non-MCP services as "virtual servers" so you can register any REST API or function endpoint and expose it under MCP semantics
17-
* **Adapt** arbitrary REST/HTTP APIs into MCP tools with JSON-Schema input validation, retry/rate-limit policies and transparent JSON-RPC invocation
18-
* **Simplify** deployments with a full admin UI, rich transports, pre-built DX pipelines and production-grade observability
35+
* Federation across multiple MCP and REST services
36+
* Virtualization of legacy APIs as MCP-compliant tools and servers
37+
* Transport over HTTP, JSON-RPC, WebSocket, SSE, and stdio
38+
* A live Admin UI for real-time management and configuration
39+
* Built-in auth, observability, retries, and rate-limiting
40+
* Scalable deployments via Docker or PyPI, Redis-backed caching, and multi-cluster federation
1941

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

2244
---
2345

24-
## Features
46+
<details>
47+
<summary><strong>🔌 Gateway Layer with Protocol Flexibility</strong></summary>
2548

26-
### Core
49+
* Sits in front of any MCP server or REST API
50+
* Lets you choose your MCP protocol version (e.g., `2025-03-26`)
51+
* Exposes a single, unified interface for diverse backends
2752

28-
* **Full MCP 2025-03-26**: initialize, ping, notify, completion, sampling (SSE), plus JSON-RPC fallback
29-
* **Gateway Layer**: sits alongside or in front of MCP servers, enforcing MCP rules and consolidating multiple backends
30-
* **Multi-Transport**: HTTP/JSON-RPC, WebSocket (ping/pong), SSE (one-way + backchannel), stdio
31-
* **Federation**:
53+
</details>
3254

33-
* Auto-discover or configure peer gateways
34-
* Periodic health checks with fail-over
35-
* Transparent merging of remote registries into one catalog
36-
* **Virtual Servers**: wrap any non-MCP endpoint (REST, gRPC, function) as a managed MCP server with minimal config
37-
* **REST-to-MCP Adapter**: register any REST API as an MCP tool—automatic schema extraction, auth headers, retry/rate limits
38-
* **Resources**: templated URIs, LRU+TTL caching, MIME detection, real-time SSE subscriptions
39-
* **Prompts**: Jinja2 templates, JSON-Schema enforcement, multimodal blocks, versioning & rollback
40-
* **Tools**: MCP-native or REST-based; input validation, retry logic, rate-limit/concurrency controls
55+
<details>
56+
<summary><strong>🌐 Federation of Peer Gateways</strong></summary>
57+
58+
* Auto-discovers or configures peer gateways (via mDNS or manual)
59+
* Performs health checks and merges remote registries transparently
60+
* Supports Redis-backed syncing and fail-over
61+
62+
</details>
4163

42-
### Extras
64+
<details>
65+
<summary><strong>🧩 Virtualization of REST/gRPC Services</strong></summary>
66+
67+
* Wraps non-MCP services as virtual MCP servers
68+
* Registers tools, prompts, and resources with minimal configuration
4369

44-
* **Admin UI** (HTMX + Alpine.js + Tailwind): full CRUD for servers, tools, resources, prompts, gateways, roots & metrics
45-
* **Authentication & Authorization**: Basic, JWT Bearer, custom header schemes, per-endpoint DI
46-
* **Persistence & Migrations**: async SQLAlchemy ORM (SQLite, Postgres, MySQL, etc.), Alembic auto-migrations
47-
* **Event System**: uniform event envelopes on WS/SSE fan-out, server-side filters, backchannel hooks
48-
* **Observability & Health**: structured JSON logs, `/health` latency metrics decorator on every handler
49-
* **Developer Experience**: Makefile targets, pre-commit (`ruff`, `black`, `mypy`, `isort`), live-reload, 400+ tests, CI badges
70+
</details>
71+
72+
<details>
73+
<summary><strong>🔁 REST-to-MCP Tool Adapter</strong></summary>
74+
75+
* Adapts REST APIs into tools with:
76+
77+
* Automatic JSON Schema extraction
78+
* Support for headers, tokens, and custom auth
79+
* Retry, timeout, and rate-limit policies
80+
81+
</details>
82+
83+
<details>
84+
<summary><strong>🧠 Unified Registries</strong></summary>
85+
86+
* **Prompts**: Jinja2 templates, multimodal support, rollback/versioning
87+
* **Resources**: URI-based access, MIME detection, caching, SSE updates
88+
* **Tools**: Native or adapted, with input validation and concurrency controls
89+
90+
</details>
91+
92+
<details>
93+
<summary><strong>📈 Admin UI, Observability & Dev Experience</strong></summary>
94+
95+
* Live Admin UI built with HTMX + Alpine.js
96+
* Auth: Basic, JWT, or custom schemes
97+
* Structured logs, health endpoints, metrics
98+
* 400+ tests, Makefile targets, live reload, pre-commit hooks
99+
100+
</details>
50101

51102
---
52103

@@ -61,7 +112,8 @@ python3 -m venv .venv
61112
. ./.venv/bin/activate
62113

63114
# Install mcp-contextforge-gateway
64-
pip install mcp-contextforge-gateway
115+
pip install mcp-contextforge-gateway # from pypi
116+
#pip install . # or install from latest github code after cloning repo
65117

66118
# Run mcpgateway with default options (binds to 127.0.0.1:4444) with admin:changeme
67119
mcpgateway # login to http://127.0.0.1:4444
@@ -77,7 +129,7 @@ export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --
77129

78130
# Run a local MCP Server (github) listening on SSE http://localhost:8000/sse
79131
pip install uvenv
80-
npx -y supergateway --stdio "uvenv run mcp-server-git"
132+
npx -y supergateway --stdio "uvenv run mcp-server-git" # requires node.js and npx
81133
# or time: npx -y supergateway --stdio "uvenv run mcp_server_time -- --local-timezone=Europe/Dublin" --port 8002
82134

83135
#--------------------------------------------
@@ -346,7 +398,7 @@ pipx install uv
346398
uv venv ~/.venv/mcpgateway
347399
source ~/.venv/mcpgateway/bin/activate
348400

349-
# Install the gateway package very quicmcpkly
401+
# Install the gateway package using uv
350402
uv pip install mcp-contextforge-gateway
351403

352404
# Launch wrapper
@@ -557,7 +609,7 @@ A `make compose-up` target is provided along with a [docker-compose.yml](docker-
557609

558610
> ⚠️ If any required `.env` variable is missing or invalid, the gateway will fail fast at startup with a validation error via Pydantic.
559611

560-
You can get started by copying the provided [.env.examples](.env.example) to `.env` and making the necessary edits to fit your environment.
612+
You can get started by copying the provided [.env.example](.env.example) to `.env` and making the necessary edits to fit your environment.
561613

562614
<details>
563615
<summary><strong>🔧 Environment Configuration Variables</strong></summary>
@@ -1605,6 +1657,64 @@ devpi-web - Open devpi web interface
16051657
```
16061658
</details>
16071659
1660+
## 🔍 Troubleshooting
1661+
1662+
<details>
1663+
<summary><strong>Port publishing on WSL2 (rootless Podman & Docker Desktop)</strong></summary>
1664+
1665+
### Diagnose the listener
1666+
1667+
```bash
1668+
# Inside your WSL distro
1669+
ss -tlnp | grep 4444 # Use ss
1670+
netstat -anp | grep 4444 # or netstat
1671+
```
1672+
1673+
*Seeing `:::4444 LISTEN rootlessport` is normal* – the IPv6 wildcard
1674+
socket (`::`) also accepts IPv4 traffic **when**
1675+
`net.ipv6.bindv6only = 0` (default on Linux).
1676+
1677+
### Why localhost fails on Windows
1678+
1679+
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.
1680+
1681+
#### Fix (Podman rootless)
1682+
1683+
```bash
1684+
# Inside the WSL distro
1685+
echo "wsl" | sudo tee /etc/containers/podman-machine
1686+
systemctl --user restart podman.socket
1687+
```
1688+
1689+
`ss` should now show `0.0.0.0:4444` instead of `:::4444`, and the
1690+
service becomes reachable from Windows *and* the LAN.
1691+
1692+
#### Fix (Docker Desktop > 4.19)
1693+
1694+
Docker Desktop adds a "WSL integration" switch per-distro.
1695+
Turn it **on** for your distro, restart Docker Desktop, then restart the
1696+
container:
1697+
1698+
```bash
1699+
docker restart mcpgateway
1700+
```
1701+
1702+
</details>
1703+
1704+
<details>
1705+
<summary><strong>Gateway starts but immediately exits ("Failed to read DATABASE_URL")</strong></summary>
1706+
1707+
Copy `.env.example` to `.env` first:
1708+
1709+
```bash
1710+
cp .env.example .env
1711+
```
1712+
1713+
Then edit `DATABASE_URL`, `JWT_SECRET_KEY`, `BASIC_AUTH_PASSWORD`, etc.
1714+
Missing or empty required vars cause a fast-fail at startup.
1715+
1716+
</details>
1717+
16081718
## Contributing
16091719
16101720
1. Fork the repo, create a feature branch.
@@ -1631,17 +1741,17 @@ Licensed under the **Apache License 2.0** – see [LICENSE](./LICENSE)
16311741
Special thanks to our contributors for helping us improve ContextForge MCP Gateway:
16321742
16331743
<a href="https://github.com/ibm/mcp-context-forge/graphs/contributors">
1634-
<img alt="Contributors list" src="https://contrib.rocks/image?repo=ibm/mcp-context-forge" />
1744+
<img src="https://contrib.rocks/image?repo=ibm/mcp-context-forge&max=100&anon=0&columns=10" />
16351745
</a>
16361746
16371747
## Star History and Project Activity
16381748
16391749
[![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)
16401750
1641-
[![Forks](https://img.shields.io/github/forks/ibm/mcp-context-forge?style=social)](https://github.com/ibm/mcp-context-forge/network/members)
1642-
1643-
<!-- [![Issues Open](https://img.shields.io/github/issues/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/issues) -->
1644-
1645-
**PyPi Downloads:**
1646-
1647-
[![PyPI](https://img.shields.io/pypi/v/mcp-contextforge-gateway)](https://pypi.org/project/mcp-contextforge-gateway/) [![PyPI Downloads](https://img.shields.io/pypi/dm/mcp-contextforge-gateway)](https://pepy.tech/project/mcp-contextforge-gateway)
1751+
<!-- === Usage Stats === -->
1752+
[![PyPi Downloads](https://static.pepy.tech/badge/mcp-contextforge-gateway/month)](https://pepy.tech/project/mcp-contextforge-gateway)&nbsp;
1753+
[![Stars](https://img.shields.io/github/stars/ibm/mcp-context-forge?style=social)](https://github.com/ibm/mcp-context-forge/stargazers)&nbsp;
1754+
[![Forks](https://img.shields.io/github/forks/ibm/mcp-context-forge?style=social)](https://github.com/ibm/mcp-context-forge/network/members)&nbsp;
1755+
[![Contributors](https://img.shields.io/github/contributors/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/graphs/contributors)&nbsp;
1756+
[![Last Commit](https://img.shields.io/github/last-commit/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/commits)&nbsp;
1757+
[![Open Issues](https://img.shields.io/github/issues/ibm/mcp-context-forge)](https://github.com/ibm/mcp-context-forge/issues)&nbsp;

docs/docs/architecture/roadmap.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,80 @@
22

33
---
44

5+
## 🌐 Federation & Routing
6+
7+
### 🧭 Epic: Streamable HTTP Transport (Protocol Revision 2025-03-26)
8+
9+
> **Note:** stdio and the legacy HTTP+SSE transports are already supported; this epic adds the new Streamable HTTP transport per the 2025-03-26 spec.
10+
11+
* **HTTP POST Messaging**
12+
**As** an MCP client
13+
**I want** to send every JSON-RPC request, notification, or batch in a separate HTTP POST to the MCP endpoint, with `Accept: application/json, text/event-stream`
14+
**So that** the server can choose between immediate JSON replies or initiating an SSE stream.
15+
16+
* **SSE-Backed Streaming on POST**
17+
**As** a developer
18+
**I want** the server, upon receiving request-bearing POSTs, to return `Content-Type: text/event-stream` and open an SSE stream—emitting JSON-RPC responses, server-to-client requests, and notifications until complete—before closing the stream
19+
**So that** clients can consume large or real-time payloads incrementally without buffering.
20+
21+
* **Unsolicited Server Notifications via GET**
22+
**As** a client
23+
**I want** to open an SSE stream with a GET (using `Accept: text/event-stream`) to the same MCP endpoint
24+
**So that** I can receive unsolicited server-to-client messages independently of POST calls.
25+
26+
* **Session Management & Resumability**
27+
**As** an operator
28+
**I want** the server to issue a secure `Mcp-Session-Id` on Initialize, require it on subsequent calls (400 if missing), allow DELETE to terminate, and support SSE resumability via `Last-Event-ID` headers
29+
**So that** clients can manage, resume, and explicitly end long-running sessions robustly.
30+
31+
* **Security & Compatibility**
32+
**As** a platform admin
33+
**I want** to validate `Origin` headers, bind to localhost by default, and enforce authentication against DNS rebinding—while optionally preserving the legacy HTTP+SSE endpoints for backward compatibility with 2024-11-05 clients
34+
**So that** we uphold security best practices and maintain dual-transport support.
35+
36+
---
37+
38+
## 🌐 Federation & Routing
39+
40+
### 🧭 Epic: A2A Transport Support
41+
42+
Enable full-duplex, application-to-application (A2A) integration so that virtual servers and gateways can speak A2A natively.
43+
44+
* **A2A Gateway Registration**
45+
**As** a platform admin
46+
**I want** to register A2A-enabled servers as gateways (in addition to HTTP/SSE/WS)
47+
**So that** I can federate A2A backends alongside standard MCP peers.
48+
49+
* **A2A Tool Invocation**
50+
**As** a developer
51+
**I want** to call A2A servers as tools via the A2A protocol
52+
**So that** A2A-native services appear in my tool catalog and handle messages over A2A transports.
53+
54+
* **Expose Virtual Servers via A2A**
55+
**As** an operator
56+
**I want** to expose virtual servers (i.e. REST-wrapped MCP servers) over the A2A transport
57+
**So that** clients that only support A2A can invoke those servers transparently.
58+
59+
---
60+
61+
## ⚙️ Lifecycle & Management
62+
63+
### 🧭 Epic: Virtual Server Protocol Version Selection
64+
65+
Allow choosing which MCP protocol version each virtual server uses.
66+
67+
* **Per-Server Protocol Version**
68+
**As** a platform admin
69+
**I want** to specify the MCP protocol version (e.g. 2025-03-26 or earlier) on each virtual server
70+
**So that** clients requiring legacy behavior can continue to work without affecting others.
71+
72+
* **Protocol Compatibility Testing**
73+
**As** a developer
74+
**I want** to validate a virtual server's behavior against multiple protocol versions in the Admin UI
75+
**So that** I can catch breaking changes before rolling out new servers.
76+
77+
---
78+
579
## 🔐 Authentication & Identity
680

781
### 🧭 [#87 Epic: JWT Token Catalog with Per-User Expiry and Revocation](https://github.com/IBM/mcp-context-forge/issues/87)

smoketest.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,16 +314,18 @@ def step_8_invoke_tool():
314314
logging.info("✅ Tool invocation returned time")
315315

316316

317-
318317
def step_9_version_health():
319318
health = request("GET", "/health").json()["status"].lower()
320319
assert health in ("ok", "healthy"), f"Unexpected health status: {health}"
321320
ver = request("GET", "/version").json()["app"]["name"]
322321
logging.info("✅ Health OK – app %s", ver)
323322

324323

324+
def step_10_cleanup_gateway(gid: int | None = None):
325+
if gid is None:
326+
logging.warning("🧹 No gateway ID; nothing to delete")
327+
return
325328

326-
def step_10_cleanup_gateway(gid: int):
327329
request("DELETE", f"/gateways/{gid}")
328330
assert all(g["id"] != gid for g in request("GET", "/gateways").json())
329331
logging.info("✅ Gateway deleted")
@@ -388,8 +390,11 @@ def main():
388390
fn(args.restart_time_server) # type: ignore[arg-type]
389391
elif name == "register_gateway":
390392
gid = fn() # type: ignore[func-returns-value]
391-
elif name == "cleanup_gateway" and gid is not None:
392-
fn(gid) # type: ignore[arg-type]
393+
elif name == "cleanup_gateway":
394+
if gid is None:
395+
logging.warning("🧹 Skipping gateway‐deletion: no gateway was ever registered")
396+
else:
397+
fn(gid) # type: ignore[arg-type]
393398
else:
394399
fn()
395400
logging.info("\n✅✅ ALL STEPS PASSED")
@@ -401,6 +406,5 @@ def main():
401406
cleanup()
402407

403408

404-
405409
if __name__ == "__main__":
406410
main()

0 commit comments

Comments
 (0)