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_URLMCP_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 uvuvx 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

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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_combiner-0.13.4.tar.gz (302.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_combiner-0.13.4-py3-none-any.whl (162.2 kB view details)

Uploaded Python 3

File details

Details for the file mcp_combiner-0.13.4.tar.gz.

File metadata

  • Download URL: mcp_combiner-0.13.4.tar.gz
  • Upload date:
  • Size: 302.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcp_combiner-0.13.4.tar.gz
Algorithm Hash digest
SHA256 bf43bc512f10a1755a201fe559b6e86c565b012ce9fcc4f656565a4f666d7fc2
MD5 83c2bced87030cd4871c3b403e0fd54e
BLAKE2b-256 d97fcb4808116d17dfae35c7de3897b928edb14d06b6509e2aac6234dfc0f63a

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_combiner-0.13.4.tar.gz:

Publisher: release.yml on georgeharker/mcp-companion

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_combiner-0.13.4-py3-none-any.whl.

File metadata

  • Download URL: mcp_combiner-0.13.4-py3-none-any.whl
  • Upload date:
  • Size: 162.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcp_combiner-0.13.4-py3-none-any.whl
Algorithm Hash digest
SHA256 6d6c56bb3d8757c466e6e6163ec92e269ef7e5f8007c3205f68eaeedb485f64b
MD5 37176223d6928606b220193663b36834
BLAKE2b-256 86bd0b40e64ed668ce661b1a7fabc2ab5554f1053d1f05bbadc999d952991d98

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_combiner-0.13.4-py3-none-any.whl:

Publisher: release.yml on georgeharker/mcp-companion

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.13.4 This release

2 files

0.13.3

2 files

0.13.2

2 files

0.13.1

2 files

0.13.0

2 files

0.12.0

2 files

0.11.2

2 files

0.11.1

2 files

0.11.0

2 files

0.10.2

2 files

0.10.1

2 files

0.10.0

2 files

0.9.4

2 files

0.9.3

2 files

0.9.2

2 files

0.9.1

2 files

0.8.4

2 files

0.8.3

2 files

0.8.2

2 files

0.8.0

2 files

0.7.6

2 files

0.7.5

2 files

0.7.4

2 files

0.7.3

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.2

2 files

0.6.0

2 files

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