| Crates.io | rmcp-mux |
| lib.rs | rmcp-mux |
| version | 0.3.1 |
| created_at | 2025-12-05 03:16:50.41395+00 |
| updated_at | 2025-12-05 04:23:18.738446+00 |
| description | MCP server multiplexer - single server, multiple clients via Unix sockets |
| homepage | |
| repository | https://github.com/Loctree/rmcp-mux |
| max_upload_size | |
| id | 1967625 |
| size | 418,414 |
A Rust library and daemon that lets many MCP clients reuse a single STDIO server process (e.g. npx @modelcontextprotocol/server-memory) over a Unix socket. It rewrites JSON-RPC IDs per client, caches initialize, restarts the child on failure, and cleans up the socket on exit.
NEW in 0.3.0: Now available as an embeddable library! Integrate MCP multiplexing directly into your Rust application.
--cmd ...--max-active-clients (default 5)--status-file) – PID, restarts, queue depth for automation--tray) – live server status, client/pending counts, restart reasonAdd to your Cargo.toml:
[dependencies]
rmcp-mux = { version = "0.3", default-features = false }
use rmcp_mux::{MuxConfig, run_mux_server};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = MuxConfig::new("/tmp/my-mcp.sock", "npx")
.with_args(vec!["-y".into(), "@anthropic/mcp-server".into()])
.with_max_clients(10)
.with_service_name("my-mcp-server");
run_mux_server(config).await
}
Perfect for tools like loctree that need to run multiple MCP services:
use rmcp_mux::{MuxConfig, spawn_mux_server, MuxHandle};
use std::time::Duration;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Define your MCP services
let services = vec![
("memory", "/tmp/mcp-memory.sock", "npx", vec!["@mcp/server-memory"]),
("filesystem", "/tmp/mcp-fs.sock", "npx", vec!["@mcp/server-filesystem"]),
("brave-search", "/tmp/mcp-brave.sock", "npx", vec!["@mcp/server-brave"]),
];
// Spawn all services in a single process
let mut handles: Vec<MuxHandle> = Vec::new();
for (name, socket, cmd, args) in services {
let config = MuxConfig::new(socket, cmd)
.with_args(args.into_iter().map(String::from).collect())
.with_service_name(name)
.with_request_timeout(Duration::from_secs(60));
handles.push(spawn_mux_server(config).await?);
}
println!("Running {} MCP services in single process", handles.len());
// Wait for all to complete (or shutdown signal)
for handle in handles {
handle.wait().await?;
}
Ok(())
}
use rmcp_mux::{MuxConfig, spawn_mux_server};
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let handle = spawn_mux_server(
MuxConfig::new("/tmp/mcp.sock", "my-server")
).await?;
// Run for 60 seconds, then shutdown
sleep(Duration::from_secs(60)).await;
handle.shutdown();
handle.wait().await?;
Ok(())
}
For advanced integration with your own shutdown logic:
use rmcp_mux::{MuxConfig, ResolvedParams, run_mux_with_shutdown};
use tokio_util::sync::CancellationToken;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let shutdown = CancellationToken::new();
let shutdown_clone = shutdown.clone();
// Your own shutdown logic
tokio::spawn(async move {
// Wait for your application's shutdown signal
// ...
shutdown_clone.cancel();
});
let config = MuxConfig::new("/tmp/mcp.sock", "my-server");
run_mux_with_shutdown(config.into(), shutdown).await
}
use rmcp_mux::check_health;
async fn verify_service() -> bool {
check_health("/tmp/mcp-memory.sock").await.is_ok()
}
| Feature | Default | Description |
|---|---|---|
cli |
✓ | CLI binary, wizard, scan commands |
tray |
✓ | System tray icon support |
For library-only usage (minimal dependencies):
[dependencies]
rmcp-mux = { version = "0.3", default-features = false }
# Build
cargo build --release
# Run with memory server
./target/release/rmcp-mux \
--socket /tmp/mcp-memory.sock \
--cmd npx -- @modelcontextprotocol/server-memory \
--max-active-clients 5
# Connect via proxy (for MCP hosts expecting STDIO)
rmcp-mux-proxy --socket /tmp/mcp-memory.sock
cargo build --release
# Binaries: target/release/rmcp-mux, target/release/rmcp-mux-proxy
curl -fsSL https://raw.githubusercontent.com/Loctree/rmcp-mux/main/tools/install.sh | sh
Environment overrides:
INSTALL_DIR – wrapper location (default: $HOME/.local/bin)CARGO_HOME – cargo home (default: ~/.cargo)MUX_REF – branch/tag/commit (default: main)MUX_NO_LOCK=1 – skip --locked flagIf your MCP host needs a STDIO command, use the bundled proxy instead of socat:
rmcp-mux-proxy --socket /tmp/mcp-memory.sock
Default path: ~/.codex/mcp.json (override with --config <path>). Parser auto-detects by extension.
JSON:
{
"servers": {
"general-memory": {
"socket": "~/mcp-sockets/general-memory.sock",
"cmd": "npx",
"args": ["@modelcontextprotocol/server-memory"],
"max_active_clients": 5,
"max_request_bytes": 1048576,
"request_timeout_ms": 30000,
"restart_backoff_ms": 1000,
"restart_backoff_max_ms": 30000,
"max_restarts": 5,
"status_file": "~/.rmcp_servers/rmcp-mux/status.json",
"lazy_start": false,
"tray": true,
"service_name": "general-memory"
}
}
}
YAML:
servers:
general-memory:
socket: "~/mcp-sockets/general-memory.sock"
cmd: "npx"
args: ["@modelcontextprotocol/server-memory"]
max_active_clients: 5
tray: true
TOML:
[servers.general-memory]
socket = "~/mcp-sockets/general-memory.sock"
cmd = "npx"
args = ["@modelcontextprotocol/server-memory"]
max_active_clients = 5
tray = true
./target/release/rmcp-mux --config ~/.codex/mcp.json --service general-memory
CLI flags override config values (e.g. --socket, --cmd, --tray).
| Parameter | Default | Description |
|---|---|---|
socket |
required | Unix socket path |
cmd |
required | MCP server command |
args |
[] |
Arguments for command |
max_active_clients |
5 |
Concurrent client limit |
lazy_start |
false |
Defer child spawn until first request |
max_request_bytes |
1048576 |
Max request size (1 MiB) |
request_timeout_ms |
30000 |
Request timeout (30s) |
restart_backoff_ms |
1000 |
Initial restart delay (1s) |
restart_backoff_max_ms |
30000 |
Max restart delay (30s) |
max_restarts |
5 |
Restart limit (0 = unlimited) |
tray |
false |
Enable tray icon |
status_file |
none | Path for JSON status snapshots |
The wizard provides a three-step guided flow for configuring rmcp-mux and rewiring MCP clients:
rmcp-mux wizard --config ~/.codex/mcp-mux.toml
ps command[✓] selected / [ ] unselected[C] config-based / [D] detected processControls:
Space – toggle server selectionTab – switch to editor panel↑/↓ – navigate listn – proceed to Step 2~/.codex/config.toml)~/Library/Application Support/Cursor/...)~/Library/Application Support/Code/...)~/.config/Claude/claude_config.json)~/Library/Application Support/JetBrains/LLM/mcp.json)[rewired] or [not rewired]Controls:
Space – toggle client selection for rewiringn – proceed to Step 3p – go back to Step 1pbcopy on macOS)Features:
.bak backup files for all modified configs--dry-run mode to preview changes without writingrmcp-mux wizard \
--config ~/.codex/mcp-mux.toml \
--service general-memory \
--dry-run
scan – Discover and generate configs# Generate mux manifest and host snippets
rmcp-mux scan \
--manifest ~/.codex/mcp-mux.toml \
--snippet ~/.codex/mcp-mux \
--socket-dir ~/.rmcp_servers/rmcp-mux/sockets
rewire – Update host configs# Rewire a host config to use rmcp-mux proxy (creates .bak backup)
rmcp-mux rewire --host codex --socket-dir ~/.rmcp_servers/rmcp-mux/sockets
# Preview changes without writing
rmcp-mux rewire --host codex --dry-run
status – Check rewire statusrmcp-mux status --host codex --proxy-cmd rmcp-mux-proxy
health – Verify connectivity# Direct check
rmcp-mux health --socket /tmp/mcp-memory.sock --cmd npx -- @modelcontextprotocol/server-memory
# Config-based check
rmcp-mux health --config ~/.codex/mcp.json --service general-memory
proxy – STDIO proxyrmcp-mux proxy --socket /tmp/mcp-memory.sock
client_idglobal_id = c<client>:<seq>initialize hits server; response cached and fanned out to waitersinitialize calls answered from cacheRun with --tray to spawn a status icon showing:
Click "Quit mux" in the tray menu to stop the daemon.
For custom monitoring, write status snapshots:
rmcp-mux --status-file ~/.rmcp_servers/rmcp-mux/status.json ...
rmcp-mux/
├── src/
│ ├── lib.rs # Library entry point, MuxConfig, public API
│ ├── config.rs # Config types, CliOptions trait, loading
│ ├── state.rs # MuxState, StatusSnapshot, helpers
│ ├── scan.rs # Host discovery and rewiring (cli feature)
│ ├── tray.rs # Tray icon (tray feature)
│ ├── bin/
│ │ ├── rmcp_mux.rs # CLI binary → rmcp-mux (cli feature)
│ │ └── rmcp_mux_proxy.rs # STDIO proxy → rmcp-mux-proxy (cli feature)
│ ├── runtime/ # Core mux daemon (always available)
│ │ ├── mod.rs # run_mux, run_mux_internal, health_check
│ │ ├── types.rs # ServerEvent, constants
│ │ ├── client.rs # Client connection handling
│ │ ├── server.rs # Child process management
│ │ ├── proxy.rs # STDIO proxy logic
│ │ ├── status.rs # Status file writing
│ │ └── tests.rs # Runtime tests
│ └── wizard/ # Interactive TUI wizard (cli feature)
│ ├── mod.rs # Entry point
│ ├── types.rs # WizardStep, ServiceEntry, etc.
│ ├── services.rs # Server detection, health checks
│ ├── clients.rs # Client detection
│ ├── ui.rs # Ratatui drawing
│ ├── keys.rs # Key event handling
│ └── persist.rs # Config saving, rewiring
├── tools/
│ ├── install.sh # One-liner installer
│ ├── launchd/ # macOS launchd templates
│ └── githooks/ # Git hooks
├── public/
│ └── rmcp_mux_icon.png # Tray icon
└── .ai-agents/ # AI agent workspace
└── AI_GUIDELINES.md # Guidelines for AI agents
| Module | Feature | Public API |
|---|---|---|
runtime |
always | run_mux, run_mux_internal, health_check, run_proxy |
config |
always | Config, ResolvedParams, MuxConfig, CliOptions |
state |
always | MuxState, StatusSnapshot, ServerStatus |
scan |
cli | run_scan_cmd, run_rewire_cmd, run_status_cmd |
wizard |
cli | run_wizard, WizardArgs |
tray |
tray | Internal (started via MuxConfig::with_tray(true)) |
# Run all tests
cargo test
# Run tests without tray feature (for CI/headless)
cargo test --no-default-features
# Linting
cargo clippy --all-targets --all-features
# Coverage
cargo tarpaulin --all-targets --timeout 120
Test coverage includes:
Template at tools/launchd/rmcp-mux.sample.plist:
cp tools/launchd/rmcp-mux.sample.plist ~/Library/LaunchAgents/rmcp-mux.general-memory.plist
# Edit paths and user
launchctl load -w ~/Library/LaunchAgents/rmcp-mux.general-memory.plist
ratatui + crossterm – TUI wizard (pure Rust)tray-icon + image – optional tray featuretokio – async runtimermcp – JSON-RPC message codectempfile – dev-only for test fixturesBuild without optional deps:
cargo build --no-default-features
See .ai-agents/AI_GUIDELINES.md for development guidelines.
MIT