mcp-multiplexer 0.3.1

One MCP server fronting many: 7 meta-tools instead of every tool schema in context
Documentation

mcp-multiplexer

CI crates.io Listed on mcpservers.org

One MCP server fronting many. Point your AI client at the multiplexer and it presents 7 meta-tools instead of every upstream server's full tool schemas — slashing the tokens spent loading tool definitions into the model's context at session start.

What it does

Every MCP server you configure pushes all of its tool schemas into the model's context up front. With a dozen servers that's tens of thousands of tokens the model mostly never uses. mcp-multiplexer is one MCP server (stdio command- based and remote HTTP upstreams) that exposes only:

Meta-tool What it returns
list_servers Overview: name, status, tool count, instructions
list_tools(server) Tool names, one-line descriptions, annotations — no schemas
search_tools(query, server?, limit=5) Matching tools with full input schemas
describe_tool(server, tool) One exact tool's full input schema
call_tool(server, tool, arguments) Proxied call; results returned verbatim
refresh_tools(server?) Reconnect and rebuild the tool index
authorize_server(server, pasted_url?) Start/complete OAuth login for a server

The model discovers tools lazily — list and search first, fetch a full schema only when it's about to call. The tool index is cached at ~/.cache/mcp-multiplexer/index.json for instant startup, and upstream servers connect lazily.

Install

Prebuilt binary (Linux x86_64/ARM64, macOS Intel/ARM, Windows x86_64 — no Rust needed): grab the archive for your platform from Releases and put mcp-multiplexer on your PATH.

# example: Linux x86_64
curl -L https://github.com/johgirard/mcp-multiplexer/releases/latest/download/mcp-multiplexer-x86_64-unknown-linux-musl.tar.gz | tar xz
sudo install mcp-multiplexer /usr/local/bin/

From source:

cargo install mcp-multiplexer

Either also installs mcp-mock, a tiny echo server used by the test suite — harmless, ignore it.

Configuration

Standard mcpServers format (Claude Code / Claude Desktop compatible), plus per-server extras:

  • expose: boolean — this server's tools also appear directly as server__tool, bypassing the meta-tools. See Hybrid mode: expose.
  • allow: list of exact names or prefix* globs — only these tools are visible.
  • deny: list, always wins over allow.
  • connect_timeout: seconds — connection/startup timeout (default 10). Raise it for slow-to-start local servers, e.g. uvx --from git+… that builds on every cold start.

Strings in command, args, env, url, and headers support ${VAR} environment expansion (same as Claude Code). An unset variable or unclosed ${ fails startup with a clear error — so keep secrets out of the config:

{
  "$schema": "https://raw.githubusercontent.com/johgirard/mcp-multiplexer/main/schema.json",
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/me/docs"],
      "allow": ["read_file", "list_directory"]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
    },
    "web": {
      "url": "https://example.com/mcp",
      "headers": { "Authorization": "Bearer ${API_TOKEN}" },
      "deny": ["admin_*"]
    },
    "linear": {
      "url": "https://mcp.linear.app/mcp",
      "oauth": true
    },
    "fast": {
      "command": "mcp-fast-server",
      "expose": true
    }
  }
}

Run with mcp-multiplexer --config /path/to/.mcp.json (defaults to ./.mcp.json).

Hybrid mode: expose

Multiplexing trades a discovery hop (list_toolsdescribe_toolcall_tool) for a near-empty startup context. For servers you call every session, many times, that hop is pure overhead — you already know the tool, and its schema costs a handful of tokens. Set "expose": true and that server's tools mount directly as first-class server__tool tools, schema and all, next to the meta-tools:

"serena": { "command": "uvx", "args": ["serena", "start-mcp-server"], "expose": true }

The model then calls serena__find_symbol like any directly-connected tool — no search, no describe, no proxy hop. The server stays reachable through the meta-tools too, and allow/deny still apply. Rule of thumb: multiplex the fleet, expose the favorites.

OAuth

Remote servers that speak OAuth 2.1 (the MCP authorization spec) work with "oauth": true on a url server — no other setup in the common case:

  • First use fails with an authorization URL. Open it, approve; a temporary 127.0.0.1 listener completes the exchange. Retry the call and it works. The model can also drive this itself via the authorize_server meta-tool.
  • Tokens live in ~/.cache/mcp-multiplexer/tokens.json (mode 0600), refresh automatically, and survive restarts — authorize once per server.

Scopes are auto-discovered; dynamic client registration is used when the provider supports it — and self-heals when a provider forgets the registration. Providers that delegate auth to a different domain than the MCP endpoint (cross-domain issuers) work out of the box. Full guide — headless paste flow, oauth_client_id / oauth_scopes / oauth_redirect_port tuning, provider notes (GitLab's group toggle gotcha), troubleshooting: docs/oauth.md.

Claude Code

Replace all your mcpServers entries with one pointing at the multiplexer:

{
  "mcpServers": {
    "mux": {
      "command": "mcp-multiplexer",
      "args": ["--config", "/home/me/.mcp.json"]
    }
  }
}

Plugin (does this for you): this repo is a Claude Code plugin whose setup skill installs the binary, migrates your existing servers into a multiplexer config (secrets become ${VAR} references, OAuth servers get flagged), rewires your client config, and verifies:

/plugin marketplace add JohGirard/mcp-multiplexer
/plugin install mcp-multiplexer@mcp-multiplexer

Then ask Claude to "set up mcp-multiplexer" (or run /mcp-multiplexer:setup).

When upstream tools change

The index is built on first connect and cached on disk. If an upstream server adds or removes tools, call refresh_tools (optionally with a server name) to re-index — no restart needed. call_tool also self-heals: a failed call triggers one reconnect, re-index, and retry before surfacing the error.

Docker

docker build -t mcp-multiplexer .
docker run -i -v $HOME/.mcp.json:/config/.mcp.json:ro mcp-multiplexer

Docker mode is for HTTP/remote upstreams only — stdio upstreams need their runtimes (node, uv, …) inside the image.

Logging

  • --log-file <path> appends logs to a file; otherwise logs go to stderr (stdout is protocol-only — do not pipe or redirect it).
  • RUST_LOG env var controls the level (e.g. RUST_LOG=debug); --verbose is shorthand for debug logging.

Stats

mcp-multiplexer --stats prints what the mux is saving you (no server start; reads ~/.cache/mcp-multiplexer/):

Startup context per session:
  without mux: ~16333 tokens (46 tools)
  with mux:    ~553 tokens (meta-tools)
  saved:       ~15780 tokens (96%)

(example: one GitLab server) plus on-demand schema bytes served, proxied call counts, and per-meta-tool usage. Tokens are estimated as bytes/4 — a heuristic, not a real tokenizer. With the Claude Code plugin installed, /mcp-multiplexer:gain shows the same report in chat.

Non-goals

  • MCP resources and prompts (tools only).
  • Upstream sampling, elicitation, and roots.
  • No truncation of tool results — returned verbatim.
  • OAuth for url servers only (stdio servers use env for secrets).
  • No tools/list_changed notification forwarding — use refresh_tools.

Development

Contributions welcome — see CONTRIBUTING.md for setup and the CI checklist, and SECURITY.md for reporting vulnerabilities.

src/bin/mcp-mock.rs builds an mcp-mock dev binary (echo/add/fail tools) used by the integration tests.

cargo test

Debug interactively with the MCP Inspector — note the --, which keeps the inspector's own --config flag from eating ours:

npx @modelcontextprotocol/inspector --web -- \
  mcp-multiplexer --config /path/to/.mcp.json

Releases

CHANGELOG.md documents every release. Cutting one:

  1. Add a ## [X.Y.Z] section to CHANGELOG.md and bump version in Cargo.toml.
  2. Commit as chore: release vX.Y.Z, tag vX.Y.Z, push commit and tag.

The tag workflow verifies the tag matches the crate version, creates the GitHub release with the changelog section as its notes, attaches per-platform binaries with SHA256 checksums, publishes to crates.io, and pushes ghcr.io/johgirard/mcp-multiplexer images tagged latest and vX.Y.Z.

License

MIT. Free for any use, including commercial — the only requirement is keeping the copyright notice.