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-bridgethenuv tool install …(see Install below).- config env vars
MCP_BRIDGE_*→MCP_COMBINER_*(andMCP_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
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-filewins over the env var; a blank/unreadable file → stays open.- Scope:
/mcpand the control routes that mutate the running server —/sessions*(session filters) and/handover*(restart handover)./healthstays open for liveness probes. Because the control routes are gated, the combiner's own callers present the token too: themcp-combinerctl and the Neovim host's REST client both readMCP_COMBINER_AUTH_TOKEN. - A missing/wrong token gets a plain
401— noWWW-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.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_combiner-0.13.0.tar.gz.
File metadata
- Download URL: mcp_combiner-0.13.0.tar.gz
- Upload date:
- Size: 302.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ee05dc61a4fe04a45b25fd7eed2de8273239a00ea440f58c5624f37b67cb376
|
|
| MD5 |
6f6783b0ec66916a57f08811fe458a8f
|
|
| BLAKE2b-256 |
18fcc659ac1e8b07e8093b5e07e72eb0f0be12df6bccb90afaf006ab49ffb8a5
|
Provenance
The following attestation bundles were made for mcp_combiner-0.13.0.tar.gz:
Publisher:
release.yml on georgeharker/mcp-companion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_combiner-0.13.0.tar.gz -
Subject digest:
0ee05dc61a4fe04a45b25fd7eed2de8273239a00ea440f58c5624f37b67cb376 - Sigstore transparency entry: 2511828657
- Sigstore integration time:
-
Permalink:
georgeharker/mcp-companion@d4c0c9012831e9000206b205279dff60434ed587 -
Branch / Tag:
refs/tags/v0.13.0 - Owner: https://github.com/georgeharker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d4c0c9012831e9000206b205279dff60434ed587 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_combiner-0.13.0-py3-none-any.whl.
File metadata
- Download URL: mcp_combiner-0.13.0-py3-none-any.whl
- Upload date:
- Size: 161.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32ce5e94fb52886dfc72b4f9c798abe966f6afd86484d95872814d85906f4a5e
|
|
| MD5 |
e0093ebb84c9f40d03e1e5a28149190c
|
|
| BLAKE2b-256 |
415ffef0e9bd4a86369e9fc09d1cfef61b27f8767d916514021d3cc22c56ea68
|
Provenance
The following attestation bundles were made for mcp_combiner-0.13.0-py3-none-any.whl:
Publisher:
release.yml on georgeharker/mcp-companion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_combiner-0.13.0-py3-none-any.whl -
Subject digest:
32ce5e94fb52886dfc72b4f9c798abe966f6afd86484d95872814d85906f4a5e - Sigstore transparency entry: 2511828709
- Sigstore integration time:
-
Permalink:
georgeharker/mcp-companion@d4c0c9012831e9000206b205279dff60434ed587 -
Branch / Tag:
refs/tags/v0.13.0 - Owner: https://github.com/georgeharker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d4c0c9012831e9000206b205279dff60434ed587 -
Trigger Event:
push
-
Statement type: