Skip to main content

mcp-combiner

An MCP aggregator — fronts multiple MCP servers behind a single Streamable HTTP endpoint, so one connection exposes every backend server's tools. Built on FastMCP. Shareable across clients (via sharedserver), it powers the mcp-companion Neovim plugin and the mcp-combiner Claude Code plugin, and works standalone with any MCP client.

PyPI package · command · import package: mcp-combiner / mcp-combiner / mcp_combiner.

⚠️ Renamed from mcp-bridge. If you ran an earlier build:

  • command/import are now mcp-combiner / mcp_combiner; reinstall: uv tool uninstall mcp-bridge then uv tool install … (see Install below).
  • config env vars MCP_BRIDGE_* → MCP_COMBINER_* (and MCP_COMPANION_COMBINER_URL → MCP_COMPANION_COMBINER_URL).
  • OAuth token storage moved to ~/.cache/mcp-combiner/ — you'll re-authenticate each MCP server once (old tokens under ~/.cache/mcp-companion/ are no longer read).

Install

Needs only uv — uvx fetches and runs it, no venv to manage:

uvx mcp-combiner --help                                                # once published to PyPI
# before PyPI (or to track main) — the package lives in the combiner/ subdirectory:
uvx --from "git+https://github.com/georgeharker/mcp-companion#subdirectory=combiner" mcp-combiner

Or install it: uv pip install mcp-combiner (PyPI), or from the repo subdir uv pip install "git+https://github.com/georgeharker/mcp-companion#subdirectory=combiner".

Usage

mcp-combiner --config /path/to/servers.json --port 9741

Interactive resource host (mcp-app widgets)

The combiner serves interactive widget resources itself: open /ui/<token>/?resource=<uri> and it renders a sandboxed host page (provider HTML on a second loopback origin — port+1 — never same-origin with the capability-holding page), streams tool data to the widget over SSE, and gates widget-initiated tool calls through the same permission pipeline as agent calls.

A tool result whose tool definition binds a widget resource (meta.ui.resourceUri, e.g. todoist's find-tasks-by-date) holds the agent's call in flight while you interact: the found data streams into the widget, your actions run as tool calls through the combiner, and Done resolves the call with a summary of the interaction. The hold budget is 50s by default (MCP_COMBINER_UI_HOLD_TIMEOUT).

Retrieve what happened inside any widget with the meta-tools:

  • combiner__ui_sessions — open/recent widget sessions per chat
  • combiner__ui_messages — the prompts, contexts, and tool calls the widget made

Security: /ui/* is bearer-free (the path token is the capability) while /mcp and the control routes keep the bearer gate; the sandbox denies connect-src unless the widget's resource metadata declares domains. For testing, the mockserver serves a spec-speaking widget at ui://mock/mock/widget (run it with --transport http and point a mock server entry at it). Design details: docs/designs/interactive-resource-host.md.

Inbound authentication

By default the /mcp endpoint is unauthenticated — fine on loopback, but a hole the moment you bind beyond 127.0.0.1 (--host) or share the box. Set a bearer token and every request to /mcp must present Authorization: Bearer <token>:

MCP_COMBINER_AUTH_TOKEN=$(cat ~/secrets/combiner-token) \
  mcp-combiner --config … --port 9741
# or, daemon-side:
mcp-combiner --config … --auth-token-file ~/secrets/combiner-token
  • Unset (both) → endpoint stays open (default; nothing changes).
  • --auth-token-file wins over the env var; a blank/unreadable file → stays open.
  • Scope: /mcp and the control routes that mutate the running server — /sessions* (session filters) and /handover* (restart handover). /health stays open for liveness probes. Because the control routes are gated, the combiner's own callers present the token too: the mcp-combiner ctl and the Neovim host's REST client both read MCP_COMBINER_AUTH_TOKEN.
  • A missing/wrong token gets a plain 401 — no WWW-Authenticate: Bearer, so standards clients surface an honest auth error instead of falling into OAuth / Dynamic Client Registration. Clients present the token pre-emptively.

The companion clients pick the token up from the same MCP_COMBINER_AUTH_TOKEN env var (Claude Code, OpenCode, Pi, and the Neovim host) — provision it once in your environment (or via each client's documented hook) and it flows to all of them.

Claude Code caveat. Claude Code strips secret-looking env-var names (*TOKEN*/*KEY*/*SECRET*/…) from the headersHelper subprocess it spawns, so that helper cannot read MCP_COMBINER_AUTH_TOKEN from its own environment. The Claude plugin works around it: the SessionStart hook (which runs with the full, unredacted environment) relays the token to the helper via a mode-600 cc-bearer-<pid> file under $XDG_STATE_HOME/mcp-companion/, written before the session-id file and removed on SessionEnd. It's written once per SessionStart and persists across combiner reconnects, so the helper re-reads it without the hook re-running. The daemon is unaffected — it too is launched by that hook, not by a headersHelper. (mcp-companion ≥ 0.13.3.)

Reusing the gate on other FastMCP servers

The middleware lives in mcp_combiner/inbound_auth.py and is self-contained (stdlib + starlette only), so a sibling FastMCP server (e.g. cribsheet, svg-mcp) can vendor the file and gate its own /mcp. Each server names its own env var — set them to the same value for one shared secret, or distinct values for per-service isolation:

server env var
mcp-combiner MCP_COMBINER_AUTH_TOKEN
cribsheet CRIBSHEET_AUTH_TOKEN
svg-mcp SVG_MCP_AUTH_TOKEN
from inbound_auth import BearerAuthMiddleware, resolve_auth_token

token = resolve_auth_token("CRIBSHEET_AUTH_TOKEN")   # or a --auth-token-file
if token:
    app.add_middleware(
        BearerAuthMiddleware, token=token,
        is_protected=lambda path: path != "/health",  # plain servers: gate all but health
    )

The combiner, in turn, presents the backend's token when it connects — add it to that server's servers.json entry: {"auth": {"bearer": "${CRIBSHEET_AUTH_TOKEN}"}} (or a headers map). See Authentication → Bearer token in the top-level README.

Development

uv sync
pytest

Metadata

Release files for mcp-combiner 0.17.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-combiner 0.17.2
File Size Uploaded Provenance
mcp_combiner-0.17.2.tar.gz 419.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-combiner 0.17.2
File Interpreter ABI Platform
mcp_combiner-0.17.2-py3-none-any.whl Python 3 none any Details

Total release size: 689.0 kB

Release files / mcp_combiner-0.17.2.tar.gz

Download URL mcp_combiner-0.17.2.tar.gz
Size 419.1 kB
Tags Source
SHA-256 checksum
How to use checksums
fac595c7b5e695ba18ddcb8f54f3847401c3b6f120c168ad283399fd16dba809
BLAKE2b-256 checksum
How to use checksums
13f900f60a5e24ef3c98e3d78f0af809ca3dd203d569632bd3ad72e3226957c0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / mcp_combiner-0.17.2-py3-none-any.whl

Download URL mcp_combiner-0.17.2-py3-none-any.whl
Size 269.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2ebb62478a35708d3078eb26d5d4bd9e3708bade75491b7504a05fa610922f63
BLAKE2b-256 checksum
How to use checksums
fb57f3baf622244a7440c8681b4112dc2a2c8394ca82595d5a0e7938d910541e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Security for mcp-combiner 0.17.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for mcp_combiner-0.17.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.17.2 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page