
mmux
by pythonstrup
mmux
A gateway that lets many Claude Code sessions share one stdio MCP server.
Why
Claude Code spawns a fresh stdio MCP server for every session. Fifteen sessions means
fifteen copies of every server. Register them with npx -y <pkg> and each one drags a
wrapper process along, doubling the count again.
Measured on a real machine (17 sessions, 5 MCP servers): 169 processes, 6.7 GB RSS.
mmux starts each backend once and exposes it over HTTP. The cost stops scaling with session count.
| Today (stdio) | mmux | |
|---|---|---|
| 3 servers × 15 sessions | 90 processes · 3.48 GB | 7 processes · 456 MB |
| Adding sessions | grows linearly | no change |
Design
A method-agnostic frame relay. It does exactly two things:
- Rewrites request ids. Client ids are local to each client, so sessions A and B
both send
id=1. Outbound requests get a global id; responses are matched back to the original id and restored. - Intercepts
initialize. The backend is initialized once at startup. Clientinitializecalls are answered from the cached result — forwarding them would re-initialize the backend and break every session already attached.
Everything else passes through untouched, so new MCP methods need no code changes.
For contrast,
mcp-proxyregisters a handler per method and runs 1,274 lines, which is why it breaks whenever the MCP SDK shifts. mmux never parsesparams.
Configuration
~/.config/mmux/config.json:
{
"listen": "127.0.0.1:9090",
"servers": {
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"env": { "MEMORY_FILE_PATH": "/Users/me/.claude/memory.jsonl" }
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
}
}
}
Copy command, args, and env straight from your existing Claude Code config —
the MCP server cannot tell the difference between being launched by Claude Code and
being launched by mmux.
Per-server fields:
| Field | Required | Notes |
|---|---|---|
command | yes | executable; the ambient PATH is inherited |
args | argument array | |
env | added to the inherited environment, not a replacement | |
cwd | working directory |
Top level: listen (default 127.0.0.1:9090) and protocolVersion (default 2025-06-18).
Unknown fields are rejected at startup, so a typo like "comand" fails loudly instead
of being silently ignored.
Each key becomes a route: http://<listen>/<key>/mcp. Adding a server to the config
makes its endpoint appear — no code changes.
The config is read once at startup. There is no hot reload — restart mmux after editing it.
Adding a server
0. Decide whether it is safe to share. This is the only hard part; see What is safe to share. The test is whether the server keeps per-session state inside its own process. Read its source and look for accumulation on an instance field — an array it always appends to, a map keyed by something the client chose, a handle it holds open. If you find one, do not share it. When in doubt, don't: a broken tool costs more than the memory it saves.
1. Add it to the config.
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "/Users/me/data.db"]
}
2. Restart mmux and confirm the backend came up.
pkill -x mmux && mmux
curl -s localhost:9090/status | python3 -m json.tool
The new entry must show "up": true. If it stays false, run mmux -debug and read
the backend's stderr.
3. Register it with Claude Code.
claude mcp add --scope user sqlite --transport http http://127.0.0.1:9090/sqlite/mcp
Or edit ~/.claude.json directly:
Related servers

n8n
Updated todayby n8n-io
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.

mcp-server-git
OfficialUpdated todayA Model Context Protocol server providing tools to read, search, and manipulate Git repositories programmatically via LLMs

mcp-server-fetch
OfficialUpdated todayA Model Context Protocol server providing tools to fetch and convert web content for usage by LLMs