{
  "markdown": "# MCP Gateway\n\n[![CI](https://github.com/MikkoParkkola/mcp-gateway/actions/workflows/ci.yml/badge.svg)](https://github.com/MikkoParkkola/mcp-gateway/actions/workflows/ci.yml)\n[![Crates.io](https://img.shields.io/crates/v/mcp-gateway.svg)](https://crates.io/crates/mcp-gateway)\n[![Downloads](https://img.shields.io/crates/d/mcp-gateway.svg)](https://crates.io/crates/mcp-gateway)\n[![Rust](https://img.shields.io/badge/rust-1.95+-blue.svg)](https://www.rust-lang.org)\n[![License](https://img.shields.io/badge/license-PolyForm--NC%20%2B%20MIT%20core-blue.svg)](https://github.com/MikkoParkkola/mcp-gateway/blob/main/LICENSES.md)\n[![unsafe denied](https://img.shields.io/badge/unsafe-denied-success.svg)](https://github.com/rust-secure-code/safety-dance/)\n[![dependency status](https://deps.rs/repo/github/MikkoParkkola/mcp-gateway/status.svg)](https://deps.rs/repo/github/MikkoParkkola/mcp-gateway)\n[![Capabilities](https://img.shields.io/badge/REST%20capabilities-110%2B-purple.svg)](https://github.com/MikkoParkkola/mcp-gateway/tree/main/capabilities)\n[![MCP Protocol](https://img.shields.io/badge/MCP-2025--11--25-green.svg)](https://modelcontextprotocol.io)\n[![OWASP Agentic AI](https://img.shields.io/badge/OWASP_Agentic_AI-self--assessed-blue.svg)](docs/OWASP_AGENTIC_AI_COMPLIANCE.md)\n[![MITRE F3](https://img.shields.io/badge/MITRE_F3-gateway_boundary_mapped-lightgrey.svg)](docs/compliance/MITRE-F3-MAPPING.md)\n[![Glama](https://glama.ai/mcp/servers/MikkoParkkola/mcp-gateway/badge)](https://glama.ai/mcp/servers/MikkoParkkola/mcp-gateway)\n[![Quality Score](https://glama.ai/mcp/servers/MikkoParkkola/mcp-gateway/badges/score.svg)](https://glama.ai/mcp/servers/MikkoParkkola/mcp-gateway)\n[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP-0078d4?logo=visualstudiocode)](https://insiders.vscode.dev/redirect/mcp/install?name=mcp-gateway&config=%7B%22command%22%3A%22mcp-gateway%22%2C%22args%22%3A%5B%22serve%22%2C%22--stdio%22%5D%7D)\n[![Install in Cursor](https://img.shields.io/badge/Cursor-Install_MCP-black?logo=cursor)](cursor://anysphere.cursor-deeplink/mcp/install?name=mcp-gateway&config=%7B%22command%22%3A%22mcp-gateway%22%2C%22args%22%3A%5B%22serve%22%2C%22--stdio%22%5D%7D)\n\n**One gateway between your AI and every tool it needs, without flooding the context window.**\n\nMCP Gateway is a single Rust binary that sits between an AI client and all of its tools. Connect MCP servers and REST APIs behind it, and the agent sees a compact meta-surface of 14 to 16 tools instead of every backend definition. It discovers and calls backend tools on demand. A small live-agent benchmark found no completed-task token saving from that extra hop, so the value is catalog capacity plus policy and routing—not a blanket token claim. See [Benchmarks](docs/BENCHMARKS.md).\n\n![demo](demo.gif)\n\nPersonal and noncommercial use is free, including running the full gateway. Running it commercially needs a [commercial license](#license), and only a small MIT core of generic building blocks is MIT-licensed.\n\n## The problem this removes\n\nEvery MCP tool an AI client connects costs roughly 150 tokens of context overhead, loaded into every request whether the tool gets used or not. Connect 20 servers with 100 tools between them and you spend about 15,000 tokens before the conversation starts. Context limits then force a second cost: you have to decide up front which tools to connect and leave the rest out, so the agent makes worse decisions because it cannot reach data you chose not to load.\n\nMCP Gateway moves the full catalog out of the exposed tool list. The agent loads a small fixed set of meta-tools, searches with `gateway_search_tools`, and invokes a backend tool with `gateway_invoke`. This creates room for larger catalogs, but the extra search hop can cost more tokens and time on a completed task.\n\n```mermaid\nflowchart LR\n    AI[\"AI client<br/>(Claude, Cursor, ...)\"]\n    subgraph GW[\"MCP Gateway (single binary)\"]\n        META[\"Compact meta-surface<br/>14-16 tools\"]\n        DISC{\"Discover on demand<br/>gateway_search_tools<br/>gateway_invoke\"}\n    end\n    T1[\"MCP backend<br/>Tavily (stdio)\"]\n    T2[\"MCP backend<br/>Context7 (http)\"]\n    C1[\"REST capability<br/>GitHub\"]\n    C2[\"REST capability<br/>Stripe\"]\n    Cn[\"110+ capabilities\"]\n\n    AI -->|\"14-16 tool defs\"| META\n    META --> DISC\n    DISC --> T1\n    DISC --> T2\n    DISC --> C1\n    DISC --> C2\n    DISC --> Cn\n```\n\n## Quick Start\n\n**Four commands:**\n\n```bash\nbrew trust --tap MikkoParkkola/tap   # Homebrew 6.0+\nbrew install MikkoParkkola/tap/mcp-gateway   # 1. install\nmcp-gateway setup wizard --configure-client  # 2. import existing servers + wire up clients\nmcp-gateway serve                            # 3. run\nmcp-gateway doctor                           # 4. verify everything is healthy\n```\n\nThat is it. Your AI clients now talk to the gateway, and the gateway routes to every backend you already had configured, at a flat `~15 tools` instead of `~150`. Start with `gateway_search_tools` from your AI client to find any backend tool, then invoke it with `gateway_invoke`.\n\n> **Nothing to import yet?** `mcp-gateway init --with-examples` writes a working `gateway.yaml` with public capabilities so you can confirm the gateway is alive before adding your own servers.\n\n**Or tell your AI assistant** (recommended):\n\n> Read https://github.com/MikkoParkkola/mcp-gateway and install mcp-gateway to consolidate all my MCP servers behind one gateway\n\nYour agent will install the binary, run the setup wizard, import your existing MCP servers, and wire itself up. This works in Claude Code, Cursor, Windsurf, Codex, and any AI with terminal access.\n\n### Install\n\n| Method | Command |\n|--------|---------|\n| **Homebrew (macOS/Linux, recommended)** | `brew install MikkoParkkola/tap/mcp-gateway` |\n| **Cargo** | `cargo install mcp-gateway` |\n| **cargo-binstall** | `cargo binstall mcp-gateway` |\n| **Direct binary download (Windows x64)** | Download `mcp-gateway-windows-x86_64.exe` from the [latest release](https://github.com/MikkoParkkola/mcp-gateway/releases/latest) |\n| **Docker** | `docker run -v $(pwd)/gateway.container.yaml:/config.yaml:ro ghcr.io/mikkoparkkola/mcp-gateway:latest --config /config.yaml` |\n\nOn Linux, the image runs as UID/GID 1001. Make an owner-only deployment copy\ninstead of changing ownership on your working config: `install -m 600\ngateway.yaml gateway.container.yaml && sudo chown 1001:1001\ngateway.container.yaml`. Do not make a credential-bearing config\nworld-readable. Docker Desktop handles bind-mount identity differently on\nmacOS and Windows.\n\n<details>\n<summary>Direct binary download</summary>\n\n```bash\n# macOS Apple Silicon\ncurl -L https://github.com/MikkoParkkola/mcp-gateway/releases/latest/download/mcp-gateway-darwin-arm64 -o mcp-gateway && chmod +x mcp-gateway\n\n# macOS Intel\ncurl -L https://github.com/MikkoParkkola/mcp-gateway/releases/latest/download/mcp-gateway-darwin-x86_64 -o mcp-gateway && chmod +x mcp-gateway\n\n# Linux x86_64\ncurl -L https://github.com/MikkoParkkola/mcp-gateway/releases/latest/download/mcp-gateway-linux-x86_64 -o mcp-gateway && chmod +x mcp-gateway\n```\n\n```powershell\n# Windows x64 (PowerShell)\nInvoke-WebRequest -Uri https://github.com/MikkoParkkola/mcp-gateway/releases/latest/download/mcp-gateway-windows-x86_64.exe -OutFile mcp-gateway.exe\n```\n\n</details>\n\n### Set up, three ways\n\n#### Option A: auto-import everything (recommended)\n\n```bash\nmcp-gateway setup wizard --configure-client\n```\n\nScans Claude Desktop, Claude Code, Cursor, Zed, Continue.dev, Codex, and running MCP processes; lets you pick which servers to import into `gateway.yaml`; previews the gateway entry; writes it into each detected client config; verifies the write; and prints backup and rollback paths when an existing client config changes. Add `--yes` to skip the prompts and import everything.\n\n#### Option B: add servers from the built-in registry\n\n48 popular MCP servers are pre-registered with the right command, args, and env-var template. `mcp-gateway add` is compatible with `claude mcp add` and `codex mcp add`:\n\n```bash\nmcp-gateway add tavily                                       # known server, fills env vars\nmcp-gateway add my-server -- npx -y @some/mcp-server --flag  # arbitrary stdio command\nmcp-gateway add --url https://mcp.sentry.dev/mcp sentry      # HTTP server\nmcp-gateway add -e API_KEY=xxx my-server -- npx my-mcp-server\n```\n\n`mcp-gateway list` shows what is configured. `mcp-gateway remove <name>` removes one.\n\n#### Option C: hand-write `gateway.yaml`\n\nFor the full schema, see the annotated [examples/gateway-full.yaml](examples/gateway-full.yaml), which covers `env_files`, `server`, `auth`, `meta_mcp`, `streaming`, `failsafe`, `cache`, `capabilities`, and `backends`. The remaining top-level sections (`playbooks`, `security`, `webhooks`, `routing_profiles`, `code_mode`, `mtls`, `key_server`, `agent_auth`, `runtime`, `marketplace`, `control_plane`, `cost_governance`) have no prose reference yet; the `Config` struct in [src/config/mod.rs](src/config/mod.rs) is the authoritative list. Minimal example:\n\n```yaml\nserver:\n  port: 39400\n\nmeta_mcp:\n  enabled: true\n\nbackends:\n  tavily:\n    command: \"npx -y @anthropic/mcp-server-tavily\"\n    description: \"Web search\"\n    env:\n      TAVILY_API_KEY: \"${TAVILY_API_KEY}\"\n\n  sentry:\n    http_url: \"https://mcp.sentry.dev/mcp\"\n    description: \"Sentry issues\"\n```\n\n### Run and verify\n\n```bash\nmcp-gateway serve                  # start the gateway\nmcp-gateway doctor                 # diagnose config, port, env vars, backend health\nmcp-gateway doctor --fix           # auto-fix issues where possible\n```\n\nThe web dashboard is at <http://localhost:39400/ui> once `serve` is running. The\noperator dashboard at `/dashboard` needs the admin credential, and a browser\ncannot send one on a navigation — so `serve` prints a single-use link to open it\nwith, on a loopback bind. A network-bound gateway prints none and is managed\nthrough `/ui` with the token instead. See\n[Opening the dashboard](docs/DEPLOYMENT.md#opening-the-dashboard).\n\n### Connect AI clients (if you skipped Option A)\n\n`setup export` writes the gateway entry into client config files for you. It auto-detects the right path per client:\n\n```bash\nmcp-gateway setup export --target all --dry-run       # preview without writing\nmcp-gateway setup export --target all                 # write, back up, verify\nmcp-gateway setup export --target claude-code         # one client\nmcp-gateway setup export --target all --watch         # regenerate on gateway.yaml changes\nmcp-gateway setup export --rollback <backup-file>     # restore one client config\n```\n\nExisting client files are backed up before mutation. The command prints the exact rollback command beside each updated client.\n\n| Client | Config path |\n|--------|-------------|\n| `claude-code` | `~/.claude.json` |\n| `claude-desktop` | platform-specific |\n| `cursor` | `.cursor/mcp.json` (workspace) |\n| `vs-code-copilot` | `.vscode/mcp.json` (workspace) |\n| `windsurf` | `~/.codeium/windsurf/mcp_config.json` |\n| `cline` | `.cline/mcp_servers.json` (workspace) |\n| `zed` | `~/.config/zed/settings.json` |\n\nModes: `--mode proxy` (HTTP), `--mode stdio` (subprocess), `--mode auto` (probe the health endpoint, then fall back).\n\n<details>\n<summary>Manual JSON snippet (if you prefer to edit by hand)</summary>\n\n```json\n{\n  \"mcpServers\": {\n    \"gateway\": {\n      \"type\": \"http\",\n      \"url\": \"http://localhost:39400/mcp\"\n    }\n  }\n}\n```\n\n</details>\n\n## Why use MCP Gateway?\n\n- **Larger catalog, smaller exposed surface.** The agent loads a fixed meta-surface instead of every backend definition. In the checked-in live run, both paths completed every task, but the meta path used 1.2–16.1% more input tokens and added one turn. See [Benchmarks](docs/BENCHMARKS.md).\n- **Unlimited tools, discovered on demand.** No more choosing which servers fit the budget. The agent searches (`gateway_search_tools`) and invokes (`gateway_invoke`) tools as it needs them.\n- **Add any REST API in minutes.** Drop in a YAML file or import an OpenAPI spec with `mcp-gateway cap import`. 110+ capabilities ship built in.\n- **Per-user identity to backends.** Multitenant backends can receive the verified end-user identity with no gateway-stored long-lived credential. See [Multitenant identity](#end-user-identity-v31).\n- **Secure by construction.** A tool-poisoning validator scans every backend tool description before it reaches the agent. SHA-256 capability pinning is optional: unpinned files load, pinned files fail closed on mismatch. OWASP Agentic AI Top 10 coverage is self-assessed in-tree, not a certification. The crate sets `#![deny(unsafe_code)]`, so any unsafe block needs an explicit `#[allow]` opt-in, with optional mTLS, message signing, and agent identity.\n- **Swap your MCP stack without losing your session.** Hot-reload backends and config in about 8ms while the AI stays connected. No restart, no lost context.\n- **Production resilience.** Circuit breakers, retries with backoff, rate limiting, and health checks keep one flaky server from taking down the whole toolchain.\n- **Dual protocol.** MCP plus an A2A (agent-to-agent) transport adapter, so the same gateway routes tool calls and cross-provider agent messages.\n\n### What MCP Gateway is, and what it is not\n\nMCP Gateway is a tool and capability **router**. It routes MCP tool, resource, and prompt traffic to backend MCP servers and to capability-backed REST APIs, and it can proxy MCP server-to-client requests like `sampling/createMessage`, `elicitation/create`, and `roots/list` back to the connected client over the existing session.\n\nIt is not a chat-completions or embeddings proxy. When a backend asks for `sampling/createMessage`, the connected client performs the model call, not the gateway. The OpenAI-compatible prompt-cache helpers exist for one narrow reason: so `gateway_invoke` can preserve `prompt_cache_key` behavior for backends that call LLM APIs internally. That boundary is deliberate. The value here is routing hundreds of tools through a small surface, not sitting in the model path.\n\nCompared with the default approach of loading every tool definition into every request, the gateway trades a one-time discovery hop for a flat, small context cost. Compared with generic transport bridges that expose one server at a time, it aggregates many backends behind one namespaced surface with integrity checks, ranking, and per-user identity.\n\n<a id=\"end-user-identity-v31\"></a>\n\n## Multitenant identity\n\nA multitenant backend (email, memory, calendar) that runs its own OIDC normally sees only \"the gateway,\" so it cannot enforce per-user access or produce a per-user audit trail. mcp-gateway propagates the verified end-user identity to the backend through one of three configured strategies. It can mint a short-lived gateway-signed assertion, forward the caller's own token, or run an RFC 8693 token exchange for OAuth-native backends. It keeps no long-lived credential for anyone. A backend marked `required` fails closed rather than serve a shared key when no verified identity is present, and per-user results stay isolated in the cache. See [ADR-007](docs/adr/ADR-007-identity-propagation.md), [ADR-008](docs/adr/ADR-008-multi-user-oauth-isolation.md), and [docs/UPGRADING-3.0.md](docs/UPGRADING-3.0.md). For the full propagation sequence, each strategy's wiring, the safety invariants, and the 2.x upgrade path, see [What is new in v3.1.0: end-user identity to backends](docs/whats-new-v3.1-identity.md).\n\n### Independent reviews\n\n- [Five MCP hot-reload tools compared](https://ruachtov.ai/blog/five-tools-mcp-restart.html): Ruach Tov Collective's BPD-based comparison of mcp-gateway against four restart-focused alternatives, with a feature matrix and architectural analysis.\n- [mcp-gateway deep dive](https://ruachtov.ai/blog/mcp-gateway-deep-dive.html): a walkthrough of the capability system, SHA-256 integrity pinning, and the v2.5 to v2.9 development arc.\n\nQuantitative claims in this README are sourced from [docs/BENCHMARKS.md](docs/BENCHMARKS.md) and the machine-readable [benchmarks/public_claims.json](benchmarks/public_claims.json), with a CI check that fails on drift. The public Trust Fabric plan is tracked in [docs/roadmap/mik-6550-trust-fabric-roadmap.md](docs/roadmap/mik-6550-trust-fabric-roadmap.md).\n\n## Why the token math matters\n\nEvery MCP tool you connect costs about 150 tokens of context overhead. Connect 20 servers with 100 tools and you have burned roughly 15,000 tokens before the first message, on definitions the AI probably will not use this turn. Worse, context limits force you to choose which tools to connect at all, so the agent makes weaker decisions because the right data is out of reach.\n\n| | Without gateway | With gateway |\n|---|----------------|--------------|\n| **Tools in context** | Every definition, every request | 16 meta-tools in the README benchmark (~1,600 tokens) |\n| **Schema footprint** | ~15,000 modeled tokens (100 tools) | ~1,600 modeled tokens before discovery; not completed-task cost |\n| **Measured task cost** | Direct path was lower at every tested size | Meta path used 1.2–16.1% more input tokens and one extra turn |\n| **Practical tool limit** | 20 to 50 tools under context pressure | Unlimited, discovered on demand |\n| **Connect a new REST API** | Build an MCP server (days) | Drop a YAML file or import an OpenAPI spec (minutes) |\n| **Changing MCP config** | Restart the AI session, lose context | Restart gateway (~8ms), session stays alive |\n| **When one tool breaks** | Cascading failures | Circuit breakers isolate it |\n\nThe gateway exposes 14 tools minimum, 16 in the README benchmark scenario, 17 when webhook status is surfaced. The base discovery quartet stays fixed; the rest are operator helpers for stats, cost, playbooks, profile control, disabled-capability visibility, reload, and webhook status.\n\n### Code Mode: two tools instead of the meta-tool set\n\nSetting `code_mode.enabled: true` makes `tools/list` return exactly two tools, `gateway_search` and `gateway_execute`, instead of the meta-tool set. Everything else is reached through those two. Tools named in `meta_mcp.surfaced_tools` are not appended in this mode, so the count stays at two however many backends are connected. Code Mode is off by default. `gateway_search` returns L0 by default (tool name, one-line purpose, score). Pass `detail=l1` or `detail=l2` for more, or `explain=true` for ranking diagnostics. `include_schema=true` is deprecated and maps to L2.\n\n```yaml\ncode_mode:\n  enabled: true\n```\n\n\n## Security\n\nConnecting N MCP servers to an agent means accepting N attack surfaces. Tool poisoning, rug pulls, and exfiltration through hidden instructions in tool descriptions are demonstrated attacks, not hypotheticals. Invariant Labs' writeup ([MCP Security Notification: Tool Poisoning Attacks](https://invariantlabs.ai/blog/mcp-security-notification-tool-poisoning-attacks)) and Simon Willison's summary ([MCP has prompt injection security problems](https://simonwillison.net/2025/Apr/9/mcp-prompt-injection/)) lay out the threat model.\n\nmcp-gateway puts every backend tool description behind one audit surface and defends it structurally:\n\n- **Tool-poisoning validator (AX-010).** Every backend tool description is scanned before it reaches the agent's context window. HIGH patterns fail closed: `<IMPORTANT>` blocks, `~/.ssh`/`~/.aws`/`id_rsa`/`.env`/`/etc/passwd`, `sidenote` exfiltration language, `curl .* https?://`, and `base64` in an exfil context. MEDIUM patterns warn: 40+ consecutive spaces, zero-width or bidi-override Unicode, and oversized descriptions. Implementation: [`src/validator/rules/tool_poisoning.rs`](src/validator/rules/tool_poisoning.rs) (19 tests).\n- **Optional SHA-256 capability hash-pinning.** `mcp-gateway cap pin <file>` writes a `sha256:` line over the file's canonical hash (`grep -v '^sha256:' capability.yaml | sha256sum` reproduces it from any shell). Unpinned files still load. A pinned file that no longer matches fails closed on load and on every watcher event.\n- **Rug-pull detection.** When a pinned capability's on-disk content changes after approval, the watcher unloads it and logs `RUG-PULL DETECTED`. The capability stays quarantined until an operator re-pins it. Implementation: [`src/capability/hash.rs`](src/capability/hash.rs) and `detect_rug_pulls` in [`src/capability/backend.rs`](src/capability/backend.rs).\n- **Centralized audit surface.** Capability YAMLs are plain text: diffable, greppable, and reviewable in a PR. The agent only ever sees the compact meta-surface, so there is no N-server tool-list pollution and no N-server attack surface.\n\nFull walkthrough, PoC snippets, and roadmap: [docs/blog/security-aware-mcp-gateway.md](docs/blog/security-aware-mcp-gateway.md).\n\n- **OWASP Agentic AI Top 10 (self-assessed).** Controls are mapped across all 10 ASI risks at the gateway boundary in-tree. That is not a certification. Hardening follow-ups are tracked separately for SBOMs, release signing, live remote attestation discovery, multi-gateway signing, SQL-sink defaults, and collusion detection. See [docs/OWASP_AGENTIC_AI_COMPLIANCE.md](docs/OWASP_AGENTIC_AI_COMPLIANCE.md).\n- **MITRE Fight Fraud Framework (F3).** A tactic-by-tactic mapping of the same gateway-boundary controls to F3 v1.1, including the two F3-native tactics (FA0001 Positioning, FA0002 Monetization). Most cash-out and card-scheme techniques are explicit gaps. See [docs/compliance/MITRE-F3-MAPPING.md](docs/compliance/MITRE-F3-MAPPING.md).\n\n### Recent additions\n\n- **OpenAPI importer.** `mcp-gateway cap import <spec-url-or-file>` turns an OpenAPI 3 spec into one validated capability YAML per operation. The full Swagger Petstore spec becomes 19 validated capability YAMLs end to end:\n  ```bash\n  mcp-gateway cap import https://petstore3.swagger.io/api/v3/openapi.json --output capabilities/ --prefix petstore\n  ```\n  22 tests across [`src/capability/openapi.rs`](src/capability/openapi.rs) and [`tests/openapi_import_tests.rs`](tests/openapi_import_tests.rs).\n\n## Architecture\n\n```mermaid\nflowchart TB\n    subgraph GW[\"MCP Gateway (:39400)\"]\n        META[\"Meta-MCP surface: 14-16 tools<br/>gateway_list_servers · gateway_list_tools<br/>gateway_search_tools · gateway_invoke\"]\n        FS[\"Failsafes: circuit breaker · retry · rate limit\"]\n        META --> FS\n    end\n    FS --> B1[\"Tavily<br/>(stdio)\"]\n    FS --> B2[\"Context7<br/>(http)\"]\n    FS --> B3[\"Pieces<br/>(sse)\"]\n    FS --> B4[\"REST capabilities<br/>(110+)\"]\n```\n\nSingle-binary gateway. An AI client talks to the compact meta-surface, and the gateway dynamically discovers and routes to backend tools. Key modules: `gateway/` (core router, OAuth, streaming, UI), `provider/` (MCP/composite/capability), `capability/` (discovery, validation), `transport/` (HTTP, stdio), `security/` (firewall, mTLS, message signing, agent identity, memory scanner), `identity_propagation/`, `key_server/`, `cost_accounting/`, `scheduler/`, `skills/`, `tool_profiles/`, `config_reload/`, and `a2a/` (A2A transport adapter).\n\n## Features\n\n### Web dashboard\n\nEmbedded web UI at `/ui`: live status, searchable tools, server health, a read-only control-plane view, and a config viewer. Operator dashboard at `/dashboard`, which needs the admin credential — on a loopback bind `serve` prints a single-use link to open it with, since a browser cannot attach an `Authorization` header to a navigation. Cost tracking at `/ui#costs`. All served from the same binary and port, with no frontend build step.\n\n### Security and governance\n\n| Feature | Description | Docs |\n|---------|-------------|------|\n| **Authentication** | Bearer tokens, API keys, explicit admin keys, per-client rate limits, and opt-in per-client circuit breakers. With auth disabled every caller over HTTP is anonymous and holds no admin; a stdio caller is admin, because the client spawned the process | [examples/per-client-tool-scopes.yaml](examples/per-client-tool-scopes.yaml) |\n| **Cross-site protection** | `Origin`, `Host` and `Sec-Fetch-Site` validation refuses web pages reaching the local port. A client that sends no `Origin` is not refused for that, but the `Host` check applies to every request, so a client reaching the gateway by a name it does not answer to is refused whether or not it is a browser | [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md#browser-access-to-the-gateway-port) |\n| **End-user identity propagation** | Three configured strategies (`identity_propagation` config): gateway-signed assertion, client-token passthrough, and RFC 8693 token exchange. Fails closed when a backend requires identity. Per-user cache isolation. Enforced on dispatch, Code Mode, and direct routes. | [docs/adr/ADR-007-identity-propagation.md](docs/adr/ADR-007-identity-propagation.md) |\n| **Per-user OAuth isolation** | Fail-closed default (v3.0): a backend that requires a per-user OAuth identity refuses a call that lacks one instead of serving a shared stored token. Opt into the previous shared-credential behavior with `auth.single_user: true` (personal gateway) or `oauth.shared_account: true` (a specific backend). Upgrading from 2.x backs up `gateway.yaml` and prints a one-time posture notice; no config changes automatically. | [docs/adr/ADR-008-multi-user-oauth-isolation.md](docs/adr/ADR-008-multi-user-oauth-isolation.md), [docs/UPGRADING-3.0.md](docs/UPGRADING-3.0.md) |\n| **Per-client tool scopes** | Allowlist or denylist tools per API key with glob patterns | [examples/per-client-tool-scopes.yaml](examples/per-client-tool-scopes.yaml) |\n| **Security firewall** | Credential redaction, prompt-injection detection, and shell/SQL/path-traversal scanning | [CHANGELOG](CHANGELOG.md#260---2026-03-13) |\n| **Cost governance** | Per-tool, per-key, daily budgets with alert thresholds (log/notify/block) | [CHANGELOG](CHANGELOG.md#260---2026-03-13) |\n| **Session sandboxing** | Per-session call limits, duration caps, backend restrictions | [CHANGELOG](CHANGELOG.md#250---2026-03-12) |\n| **mTLS** | Certificate-based auth for tool execution | [CHANGELOG](CHANGELOG.md#240---2026-02-25) |\n| **MITRE F3 mapping** | Tactic-by-tactic map of gateway-boundary controls to Fight Fraud Framework v1.1. Monetization and card-scheme techniques are listed as gaps | [docs/compliance/MITRE-F3-MAPPING.md](docs/compliance/MITRE-F3-MAPPING.md) |\n\n### Integration and discovery\n\nThe gateway ships with **110+ built-in capabilities**: weather, Wikipedia, GitHub, stock quotes, package tracking, and more. Capability YAMLs hot-reload automatically after file changes, no restart needed.\n\n| Feature | Description |\n|---------|-------------|\n| **Capability system** | REST API to MCP tool via YAML. Hot-reloaded. [110+ built-in](capabilities/). OpenAPI import supported. |\n| **Transform chains** | Namespace, filter, rename, and response transforms. [Example](examples/transform-example.yaml). |\n| **Webhooks** | GitHub/Linear/Stripe push events as MCP notifications. [Docs](docs/WEBHOOKS.md). |\n| **Auto-discovery** | Discover MCP servers from existing client configs and running processes. |\n| **Surfaced tools** | Pin high-value tools directly in `tools/list` for one-hop invocation. |\n| **Semantic search** | TF-IDF ranked search across all tool names and descriptions. |\n| **Tool profiles** | Usage analytics per tool: latency, errors, trends. Persisted to disk. |\n| **Config export** | Export sanitized config as YAML or JSON via `mcp-gateway config export`. |\n\n### Protocol and transport\n\n- **MCP version**: 2025-11-25 (latest spec)\n- **Transports**: stdio, Streamable HTTP, SSE, WebSocket\n- **Hot reload**: capability YAMLs and backends are watched and reloaded live. `server.public_url` and `control_plane.role_mapping` are re-read per request; everything else needs a restart\n- **Reload outcomes**: `gateway_reload_config` and `/ui/api/reload` report `restart_required`, and keep reporting it until a restart, for every field a reload cannot apply — which is every field outside that short live list, `auth` included. A reload that would leave the tool endpoint reachable without a credential is refused rather than applied\n- **Config discovery**: auto-finds `gateway.yaml` in cwd, `~/.config/mcp-gateway/`, and `/etc/mcp-gateway/`\n- **\"Did you mean?\"**: Levenshtein-based typo correction on tool names\n- **Tool annotations**: MCP 2025-11-25 `title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`; gateway meta-tools are fully annotated, while backend tools use the hybrid pass-through/fill policy in [ADR-003](docs/adr/ADR-003-mcp-tool-annotation-policy.md)\n- **Dynamic descriptions**: live tool and server counts in meta-tool descriptions\n- **Tunnel mode**: expose via Tailscale or pipenet without opening ports\n- **Shell completions**: `mcp-gateway completions bash|zsh|fish`\n- **Spec preview** (opt-in): filtered `tools/list` (SEP-1821), `tools/resolve` (SEP-1862), dynamic promotion\n\n### Supported backends\n\nAny MCP-compliant server works. All three transport types are supported:\n\n| Transport | Examples |\n|-----------|---------|\n| **stdio** | `@anthropic/mcp-server-tavily`, `@modelcontextprotocol/server-filesystem`, `@modelcontextprotocol/server-github` |\n| **HTTP** | Any Streamable HTTP server |\n| **SSE** | Pieces, LangChain, [GitMCP](https://gitmcp.io) (free remote docs and code search for any GitHub repo) |\n\nRemote MCP servers plug in by URL, with no extra code. See [examples/gateway-full.yaml](examples/gateway-full.yaml) for a commented GitMCP backend entry and [docs/REMOTE_BACKENDS.md](docs/REMOTE_BACKENDS.md) for a step-by-step walkthrough.\n\n## Public MCP Gateway Comparison\n\nThis table compares public, user-facing behavior, not internal roadmap scoring. MCP Gateway entries are grounded in this repo's public docs: [quickstart](QUICKSTART.md), [deployment](docs/DEPLOYMENT.md), [OWASP controls](docs/OWASP_AGENTIC_AI_COMPLIANCE.md), [TrustCard/CBOM](docs/trustcard.md), [CatalogTrustLab](docs/catalog_trust_lab.md), [adaptive ranking](docs/adaptive_ranking.md), [identity grants](docs/identity_grants.md), [ADR-007 identity propagation](docs/adr/ADR-007-identity-propagation.md), [ADR-008 multi-user OAuth isolation](docs/adr/ADR-008-multi-user-oauth-isolation.md), and the [Trust Fabric roadmap](docs/roadmap/mik-6550-trust-fabric-roadmap.md). Competitor entries are grounded in public project docs: [Docker MCP Catalog and Toolkit](https://docs.docker.com/ai/mcp-catalog-and-toolkit/), [MCPJungle README](https://github.com/mcpjungle/MCPJungle), [mcpo README](https://github.com/open-webui/mcpo), and [Supergateway README](https://github.com/supercorp-ai/supergateway).\n\n| Axis | **MCP Gateway** | **[Docker MCP Gateway / Toolkit](https://docs.docker.com/ai/mcp-catalog-and-toolkit/)** | **[MCPJungle](https://github.com/mcpjungle/MCPJungle)** | **[mcpo](https://github.com/open-webui/mcpo) / [Supergateway](https://github.com/supercorp-ai/supergateway)** |\n|---|---|---|---|---|\n| Primary job | MCP and REST capability router with a compact meta-surface | Docker-managed catalog, profiles, containerized MCP servers, and gateway | Self-hosted gateway that runs many MCP servers behind one endpoint | Protocol bridges: MCP to OpenAPI for mcpo; stdio to SSE/WS for Supergateway |\n| Install | Standalone Rust binary via cargo, Homebrew, VS Code, Cursor, and local build | Docker Desktop / Docker CLI plugin flow | Self-hosted gateway install and server registration | Python/uvx/Docker for mcpo; npm/CLI bridge for Supergateway |\n| Configuration | Wizard, local starter profile, service templates, client export, doctor JSON, backup and rollback | Docker profiles and catalog selection | Centralized server and client configuration | Per-bridge command/config for each exposed server or transport |\n| Security | OWASP Agentic AI matrix, MITRE F3 gateway-boundary mapping (gaps stated), firewall, response inspection, hash-pinned capabilities, mTLS/signing options | Verified container images with versioning, provenance, and security updates in Docker catalog | Centralized access control and observability | Transport/API exposure layer; security depends on bridge auth and deployment boundary |\n| Identity and grants | Local identity-grant contract and CLI; multi-user OAuth isolation is credential-agnostic by default (ADR-008), and a backend configured `required` fails closed rather than serve a shared credential; per-user identity propagation to backends via signed assertion, caller-token passthrough, or RFC 8693 token exchange; the OIDC key server is disabled by default, delegated-bearer acceptance is a separate opt-in, and control-plane role mappings are issuer-scoped | Docker/team controls depend on Docker organization setup | Authenticated clients and server access control | Not a grant engine; delegates identity policy to the surrounding deployment |\n| Runtime isolation | RuntimeProvider policy planning plus Docker/Podman/Kubernetes deployment paths | Container-first isolation is the core runtime model | Runs and manages MCP servers behind the gateway | Bridges existing server processes/transports rather than isolating arbitrary tools |\n| Trust metadata | TrustCard/CBOM generation, validation, TrustLab evidence, provenance stubs | Catalog packages carry image provenance and security update flow | Gateway inventory and observability focus | Protocol metadata bridge; trust metadata is not the primary product surface |\n| Discovery | Meta-MCP listing/search, ShadowRadar unmanaged-server inventory, capability registry | Docker MCP Catalog of packaged servers | Centralized discovery across configured servers | Exposes one bridged server surface at a time unless composed externally |\n| Policy and governance | Policy, grants, audit events, read-only control-plane tab/API, enterprise evidence boundary | Docker org/catalog/profile policy model | Centralized access control for teams | No broad governance plane; use with another policy layer when needed |\n| Imports and bridges | Native MCP backends plus REST capability YAML and protocol-import planning | Docker-packaged MCP server catalog | MCP server aggregation | Strong bridge story for OpenAPI, SSE, WebSocket, and stdio compatibility |\n| Ranking and routing | Safety-aware ranking, explanations, cost/latency/trust/health signals | Catalog/profile selection, not an MCP tool ranker | Gateway-level routing to configured servers | Transport routing, not semantic tool ranking |\n| Deployment | Local, team gateway, Docker Compose, systemd, launchd, a security-hardened Helm chart (non-root, seccomp, read-only rootfs), and experimental (v1alpha1) Kubernetes CRDs | Docker Desktop, Docker CLI, Docker Hub/catalog workflow | Local or shared self-hosted gateway | Local or remote bridge process beside the target MCP server |\n| Licensing | Noncommercial-default (PolyForm-NC) with a small MIT core of generic building blocks; commercial use of the runnable gateway requires a license | Docker product and repository licensing apply | See project repository license | See each bridge repository license |\n\n### vs Anthropic MCP tunnels\n\nOn 2026-05-19 Anthropic shipped [Claude Managed Agents](https://claude.com/blog/claude-managed-agents-updates) with self-hosted sandboxes (public beta) and [MCP tunnels](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview) (research preview). An MCP tunnel lets a Claude agent reach a single MCP server inside a private network through one outbound connection from a lightweight gateway, with no inbound firewall rules, no public endpoint, and end-to-end encryption.\n\nmcp-gateway and Anthropic's MCP tunnel sit at different layers and compose. The tunnel is reachability plumbing for one private MCP server. mcp-gateway is the aggregation, routing, capability-namespacing, and observability layer across many MCP and REST backends. Deploy both and mcp-gateway becomes the private MCP server that the tunnel exposes: one tunnel, one outbound connection, every backend behind it.\n\n| Concern | Anthropic MCP tunnel | mcp-gateway | Boundary |\n|---|---|---|---|\n| **Backend topology** | Single MCP server per tunnel, exposed through one outbound connection ([overview](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview)) | N-backend aggregation: 110+ REST capabilities plus multiple MCP backends behind a compact 14-16 tool meta-surface (`src/gateway/`, `capabilities/*.yaml`) | Different primitive: 1-server reachability vs many-backend aggregation |\n| **Tool routing** | Opaque pass-through; the agent sees whatever tool list the tunneled server publishes | Capability namespacing plus dynamic `gateway_search_tools` / `gateway_invoke` discovery (`src/gateway/`); SHA-256 pinning per capability (`src/capability/hash.rs`) | Different layer: transport reachability vs tool-surface curation and integrity |\n| **Observability** | Per-tunnel session telemetry from Anthropic's side | Unified `trace_id` and cost accounting across every backend invocation (`src/cost_accounting/`, `src/gateway/`) | Scope distinction: per-tunnel session vs cross-backend trace correlation |\n\nThey solve adjacent problems. A team that wants Claude Managed Agents to reach a private-network deployment of mcp-gateway uses the tunnel for reachability and mcp-gateway for fan-out, capability hygiene, OWASP Agentic AI controls, and unified cost and trace telemetry.\n\n## API\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/health` | GET | Health check with backend status; authenticated admin callers also see per-backend runtime profile lifecycle state |\n| `/mcp` | POST | Meta-MCP mode (dynamic discovery) |\n| `/mcp/{backend}` | POST | Direct backend access |\n| `/ui` | GET | Web dashboard |\n| `/ui/api/control-plane` | GET | Read-only local control-plane projection for inventory, runtime health, decisions, RBAC, and license boundaries |\n| `/dashboard` | GET | Operator dashboard. Admin only; opened with the single-use link `serve` prints on a loopback bind |\n| `/metrics` | GET | Prometheus metrics (with `--features metrics`) |\n\n## Performance\n\n| Metric | Value | Notes |\n|--------|-------|-------|\n| **Startup time** | ~8ms | Measured with `hyperfine` ([benchmarks](docs/BENCHMARKS.md)) |\n| **Binary size** | ~12-13 MB | Release build with LTO, stripped |\n| **Hot-path microbenchmarks** | Included | Criterion suite covers registry, parsing, cache-key, firewall, and semantic-search hot paths |\n| **End-to-end latency** | Backend-dependent | Measure with your real MCP servers and REST APIs rather than relying on a synthetic single number |\n\n## SKILL.md / agentskills.io compatibility\n\nMCP Gateway can ingest [Agent Skills](https://agentskills.io) and Claude Code `SKILL.md` files and expose them as discoverable skills alongside capability YAML. This lets the gateway consume any SKILL.md, whether authored locally, shipped from `agentskills.io`, or pulled from a GitHub release, and surface it through the same meta-tool surface used for capabilities.\n\n```bash\n# Import a local skill directory (auto-discovers SKILL.md + resources/)\nmcp-gateway skills import ~/.claude/skills/gws-gmail-send\n\n# Import a single SKILL.md file\nmcp-gateway skills import ./path/to/SKILL.md\n\n# Import from an agentskills.io URL\nmcp-gateway skills import https://agentskills.io/skills/my-skill/SKILL.md\n\n# List imported skills\nmcp-gateway skills list\n\n# Search by name, description, trigger, or keyword\nmcp-gateway skills search \"gmail\"\n\n# Show the full body (including any embedded code blocks)\nmcp-gateway skills show gws-gmail-send\n\n# Remove a skill\nmcp-gateway skills remove gws-gmail-send\n```\n\n**What gets parsed**\n\n- YAML frontmatter (`name`, `description`, `version`, `effort`, `allowed-tools`, `triggers`, `keywords`)\n- Markdown body, with fenced `bash`/`python`/`json` code blocks extracted as structured `SkillCodeBlock` entries\n- Progressive-disclosure resources: `SKILL.advanced.md`, `reference.md`, `README.md`, and any `resources/*.md` files in the skill directory\n\n**Security model (read-only)**\n\nImported skills are stored as data, not executed. Embedded `bash` or `python` blocks are parsed and surfaced to users and agents via `skills show`, but MCP Gateway will never run them automatically. A future release may add opt-in execution gated on per-skill user consent. To run a skill's commands today, copy them from `skills show` and run them in your own shell.\n\nRegistry location: `~/.mcp-gateway/skills.json` (override with `MCP_GATEWAY_SKILLS_REGISTRY` or `--registry`).\n\nReference: [Anthropic SKILL.md spec](https://docs.claude.com/en/docs/claude-code/skills) and [agentskills.io](https://agentskills.io).\n\n## Documentation\n\n| Document | Contents |\n|----------|----------|\n| [Quick Start](docs/QUICKSTART.md) | Zero to running in 2 minutes |\n| [Annotated config example](examples/gateway-full.yaml) | Commented `gateway.yaml` covering the most-used config sections |\n| [OAuth Configuration](docs/OAUTH_CONFIG.md) | OAuth 2.0 setup with Slack and Figma examples |\n| [Upgrading to 3.0](docs/UPGRADING-3.0.md) | Per-user OAuth isolation and identity-propagation upgrade path |\n| [Deployment Guide](docs/DEPLOYMENT.md) | Docker, systemd, TLS/mTLS, scaling |\n| [OpenAPI Import](docs/OPENAPI_IMPORT.md) | Generate capabilities from OpenAPI specs |\n| [Webhooks](docs/WEBHOOKS.md) | Event integration setup |\n| [Community Registry](docs/COMMUNITY_REGISTRY.md) | Share and install capabilities |\n| [Benchmarks](docs/BENCHMARKS.md) | Performance measurements |\n| [Changelog](CHANGELOG.md) | Release history |\n| [OWASP Agentic AI Compliance](docs/OWASP_AGENTIC_AI_COMPLIANCE.md) | Risk coverage matrix |\n| [MITRE F3 mapping](docs/compliance/MITRE-F3-MAPPING.md) | Fight Fraud Framework tactic map (PARTIAL/GAP, not a coverage claim) |\n| [ShadowRadar](docs/SHADOW_SCAN.md) | Passive local discovery and static network-rule export |\n| [Enterprise agent governance comparison](docs/competitive/willow-enterprise-agent-governance.md) | Willow/Webrix feature bar and mcp-gateway's current gaps |\n| [vs Anthropic MCP tunnels](#vs-anthropic-mcp-tunnels) | Where mcp-gateway and Anthropic's MCP tunnel compose |\n\n## Troubleshooting\n\n**Backend will not connect?** Test the command directly (`npx -y @anthropic/mcp-server-tavily`), then check gateway logs with `--log-level debug`.\n\n**Circuit breaker open?** Check `curl localhost:39400/health | jq '.backends'`. Adjust thresholds in `failsafe.circuit_breaker`.\n\n**Tools not appearing?** Verify the backend is running (`gateway_list_servers`). Tool lists are cached for 5 minutes.\n\n## Versioning and stability\n\nThis project follows [Semantic Versioning](https://semver.org/) over its\n**product surface**: the CLI, and the configuration file format. Changes to\neither are versioned accordingly — a config key that stops being accepted, or a\ncommand that changes behaviour, is a breaking change.\n\n**The Rust library API is not part of that surface.** Types are `pub` for\nmodularity and testing, not as a supported embedding API, and they may change\nin any release. The crate ships a binary; at the time of writing crates.io\nreports zero reverse dependencies. If you embed the library, pin an exact\nversion (`=3.5.1`) rather than a caret range.\n\nThis is stated explicitly because \"removing a `pub` field\" and \"breaking a\nsupported API\" are only the same thing when the API is supported. Here it is\nnot, and that needs to be published rather than assumed.\n\n## Contributing\n\n1. Fork and branch (`git checkout -b feature/your-feature`)\n2. Test (`cargo test`) and lint (`cargo fmt && cargo clippy -- -D warnings`)\n3. Open a PR against `main` with a clear description and a [CHANGELOG](CHANGELOG.md) entry\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for full details. Look for [`good first issue`](https://github.com/MikkoParkkola/mcp-gateway/labels/good%20first%20issue) or [`help wanted`](https://github.com/MikkoParkkola/mcp-gateway/labels/help%20wanted) to get started.\n\n## Ecosystem\n\nmcp-gateway is part of a suite of MCP tools:\n\n| Tool | Description |\n|------|-------------|\n| **[mcp-gateway](https://github.com/MikkoParkkola/mcp-gateway)** | **Universal MCP gateway: a compact 14-16 tool surface replaces 100+ registrations** |\n| [trvl](https://github.com/MikkoParkkola/trvl) | AI travel agent, 36 MCP tools for flights, hotels, ground transport |\n| [nab](https://github.com/MikkoParkkola/nab) | Web content extraction: fetch any URL with cookies and anti-bot bypass |\n| [axterminator](https://github.com/MikkoParkkola/axterminator) | macOS GUI automation, 34 MCP tools via the Accessibility API |\n\n## License\n\nmcp-gateway uses **mixed, per-file licensing**, and the default is Noncommercial.\n\n| Scope | License |\n|-------|---------|\n| Files whose header carries `// SPDX-License-Identifier: MIT` (below the copyright line) | MIT ([LICENSE-MIT](LICENSE-MIT)) |\n| Everything else (the default) | PolyForm Noncommercial 1.0.0 ([LICENSE-NONCOMMERCIAL](LICENSE-NONCOMMERCIAL)) |\n\nIf a file is not explicitly marked MIT, it is Noncommercial. The MIT core is a\nsmall set of simple, generic building blocks with no enterprise logic: the MCP\nprotocol types, natural-language tool search, response shaping/transforms, the\nserver design validator, the skills bridge, generic capability schema-validation\nand hashing, and the `gateway-core` primitives crate. The exact paths are in\n[`.mit-core-allowlist`](.mit-core-allowlist).\n\nWhat this means:\n\n- **Personal and noncommercial use is free**, including running the whole gateway.\n- **Running the gateway commercially requires a commercial license.** The runnable\n  gateway, covering dispatch, transport, backend management, identity, security,\n  governance, is Noncommercial. The MIT core is building blocks, not a\n  free-for-commercial gateway. See [COMMERCIAL.md](COMMERCIAL.md).\n- Versions 3.0.0–3.2.1 were published with MIT metadata for code now licensed as\n  Noncommercial from v3.3.0. Those copies stay MIT (a granted license cannot be\n  revoked) but are deprecated. See [NOTICE.md](NOTICE.md).\n\nFull model: [LICENSES.md](LICENSES.md).\n\n## Credits\n\nCreated by [Mikko Parkkola](https://github.com/MikkoParkkola). Implements [Model Context Protocol](https://modelcontextprotocol.io/) version 2025-11-25.\n\n[Changelog](CHANGELOG.md) | [Releases](https://github.com/MikkoParkkola/mcp-gateway/releases)\n",
  "bytes": 46312,
  "sha": "2dfd1f011093a13991e7c8fc7be5a9d1a1e190a75ee9d68ab818a934a2ca7dba",
  "repo_slug": "mikkoparkkola/mcp-gateway",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_mikkoparkkola_mcp_gateway_3786cd46/readme"
}