A minimal Model Context Protocol (MCP) multiplexer. It connects to several
upstream MCP servers ("backends"), merges their tools under namespaced
<backend>__<tool> names, and re-exposes them through a single MCP endpoint
(stdio or streamable HTTP). The client (e.g. Claude Code) authenticates once to
mcpmux; mcpmux holds each backend's credentials and routes calls to the right
backend.
flowchart LR
client["MCP client<br/>(Claude Code)"] -->|"one endpoint · stdio/http"| mcpmux
mcpmux["<b>mcpmux</b><br/>route + namespace as <backend>__<tool><br/>hold each backend's credentials"]
mcpmux --> b1["files · command · env secrets"]
mcpmux --> b2["github · http · bearer token"]
mcpmux --> b3["linear · http · OAuth"]
One inbound MCP server fans out to one client session per backend. Tool names get
a <backend>__ prefix to avoid collisions; calls are forwarded verbatim to the
owning backend. A backend that fails to connect is skipped (not fatal), and logs
go to stderr so stdout stays clean for the stdio transport.
- Listen transport:
stdioor streamablehttp. - Backend transports:
command(subprocess over stdio; secrets viaenv) orhttp(streamable HTTP). - Backend auth (http):
none/bearer(static token) /header(custom header + value).command— a credential helper whose stdout is the bearer token; cached and re-run only near expiry (JWTexphonored;ttlcaps opaque tokens) or on a 401/403. No browser, e.g.chainctl auth token --audience <resource>.oauth— interactive authorization-code + PKCE with dynamic client registration (RFC 7591); a browser opens once at startup, tokens refresh in memory. Options:scopes,client_name,open_browser,callback_port.
- systemd socket activation — the socket survives service restarts, so clients never hit "connection refused"; the service can run always-on or start on demand.
- Not yet: persisting OAuth tokens across restarts, aggregating resources or prompts (tools only), and authenticating the client→mcpmux hop (run it on localhost or behind your own auth proxy).
Install:
go install github.com/toabctl/mcpmux@latest # or: make buildConfigure — copy mcpmux.example.yaml and edit. ${ENV_VAR} references are
expanded at load time, so secrets stay out of the file. The config is searched in
order (first match wins; override with -c): ./mcpmux.yaml,
$XDG_CONFIG_HOME/mcpmux/config.yaml, $XDG_CONFIG_HOME/mcpmux.yaml.
Run:
mcpmux serve -c mcpmux.yaml # run the proxy
mcpmux list -c mcpmux.yaml # print the aggregated tool catalogUse from Claude Code (http):
claude mcp add --transport http mcpmux http://127.0.0.1:8080/mcpRemove the individual servers you folded into mcpmux so their tools don't appear
twice. For a stdio endpoint, let Claude Code launch it instead:
claude mcp add mcpmux -- mcpmux serve -c /path/to/mcpmux.yaml.
Run as a daemon (recommended) — make install installs the binary plus the
.service and .socket units, then:
systemctl --user enable --now mcpmux.socket mcpmux.serviceSee the comments in dist/mcpmux.service and dist/mcpmux.socket for the
always-on vs on-demand modes and the OAuth-at-startup setup.
Apache-2.0 © Thomas Bechtold