rmcp-mux – MCP Server Multiplexer
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.
Table of Contents
- Features
- Library Usage ⭐ NEW
- Quick Start (CLI)
- Installation
- Configuration
- Interactive Wizard (TUI)
- Subcommands
- Runtime Behavior
- Project Structure
- Testing
- Contributing
Features
Core
- One child process per service – spawned from
--cmd ... - Multiple clients via Unix socket – ID rewriting keeps responses matched to the right client
- Initialize caching – executed once; later clients get cached response immediately
- Concurrent requests – active client slots limited by
--max-active-clients(default 5) - Notification broadcasting – notifications sent to all connected clients
- Auto-restart – child restarts on exit; pending/waiting requests receive error on reset
- Graceful shutdown – Ctrl+C stops mux, kills child, removes socket file
Monitoring & Status
- JSON status snapshots (
--status-file) – PID, restarts, queue depth for automation - Optional tray indicator (
--tray) – live server status, client/pending counts, restart reason - Health check subcommand – verify socket reachability
Configuration
- Multi-format configs – JSON, YAML, TOML with auto-detection
- Three-step wizard – guided TUI for server detection, client rewiring, and config generation
- Host scanning – auto-detect MCP configs in Codex, Cursor, VSCode, Claude, JetBrains
Library Usage
Add to your Cargo.toml:
[]
= { = "0.3", = false }
Basic Example - Single Mux Server
use ;
async
Multiple Mux Instances (Single Process)
Perfect for tools like loctree that need to run multiple MCP services:
use ;
use Duration;
async
Programmatic Shutdown
use ;
use ;
async
With External CancellationToken
For advanced integration with your own shutdown logic:
use ;
use CancellationToken;
async
Health Check
use check_health;
async
Feature Flags
| Feature | Default | Description |
|---|---|---|
cli |
✓ | CLI binary, wizard, scan commands |
tray |
✓ | System tray icon support |
For library-only usage (minimal dependencies):
[]
= { = "0.3", = false }
Quick Start (CLI)
# Build
# Run with memory server
# Connect via proxy (for MCP hosts expecting STDIO)
Installation
From source
# Binaries: target/release/rmcp-mux, target/release/rmcp-mux-proxy
One-liner (curl | 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--lockedflag
Built-in proxy
If your MCP host needs a STDIO command, use the bundled proxy instead of socat:
Configuration
Config file formats
Default path: ~/.codex/mcp.json (override with --config <path>). Parser auto-detects by extension.
JSON:
YAML:
servers:
general-memory:
socket: "~/mcp-sockets/general-memory.sock"
cmd: "npx"
args:
max_active_clients: 5
tray: true
TOML:
[]
= "~/mcp-sockets/general-memory.sock"
= "npx"
= ["@modelcontextprotocol/server-memory"]
= 5
= true
Running with config
CLI flags override config values (e.g. --socket, --cmd, --tray).
Parameter defaults
| 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 |
Interactive Wizard (TUI)
The wizard provides a three-step guided flow for configuring rmcp-mux and rewiring MCP clients:
Step 1: Server Detection
- Detects running MCP server processes via
pscommand - Loads existing services from config file
- Displays servers with selection checkboxes:
[✓]selected /[ ]unselected[C]config-based /[D]detected process- Health status: 🟢 healthy / 🔴 unhealthy / ⚪ unknown
Controls:
Space– toggle server selectionTab– switch to editor panel↑/↓– navigate listn– proceed to Step 2
Step 2: Client Detection
- Discovers MCP client applications:
- Codex (
~/.codex/config.toml) - Cursor (
~/Library/Application Support/Cursor/...) - VSCode (
~/Library/Application Support/Code/...) - Claude (
~/.config/Claude/claude_config.json) - JetBrains (
~/Library/Application Support/JetBrains/LLM/mcp.json)
- Codex (
- Shows rewire status:
[rewired]or[not rewired] - Lists services defined in each client config
Controls:
Space– toggle client selection for rewiringn– proceed to Step 3p– go back to Step 1
Step 3: Confirmation
- Displays summary of selected servers and clients
- Save options:
- Save All – save mux config AND rewire selected clients
- Mux Only – save mux config only
- Clipboard – copy config to clipboard (
pbcopyon macOS) - Back – return to Step 2
- Exit – exit without saving
Features:
- Creates
.bakbackup files for all modified configs --dry-runmode to preview changes without writing
Wizard options
Subcommands
scan – Discover and generate configs
# Generate mux manifest and host snippets
rewire – Update host configs
# Rewire a host config to use rmcp-mux proxy (creates .bak backup)
# Preview changes without writing
status – Check rewire status
health – Verify connectivity
# Direct check
# Config-based check
proxy – STDIO proxy
Runtime Behavior
Client handling
- New client → assigned
client_id - Messages get
global_id = c<client>:<seq> - Responses demuxed back to original client/local ID
- First
initializehits server; response cached and fanned out to waiters - Later
initializecalls answered from cache
Safety guards
- Max request size – default 1 MiB
- Request timeout – default 30s with cleanup of pending calls
- Exponential restart backoff – 1s → 30s with 5 restart limit
- Lazy start – defer child spawn until first request
Error handling
- Child exit or I/O failure → restart child, clear cache/pending, send errors to affected clients
- Graceful shutdown (Ctrl+C) → stop child, delete socket
Tray Status (optional)
Run with --tray to spawn a status icon showing:
- Service name and server state
- Connected/active clients
- Pending requests
- Initialize cache state
- Restart count and reason
Click "Quit mux" in the tray menu to stop the daemon.
For custom monitoring, write status snapshots:
Project Structure
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 Visibility
| 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)) |
Testing
# Run all tests
# Run tests without tray feature (for CI/headless)
# Linting
# Coverage
Test coverage includes:
- ID rewriting and error responses
- Initialize caching and fan-out
- Reset state broadcasting
- Config loading (JSON/YAML/TOML)
- Parameter resolution and defaults
- Health checks
- Status file writing
- Proxy forwarding
launchd (macOS)
Template at tools/launchd/rmcp-mux.sample.plist:
# Edit paths and user
Dependency Notes
ratatui+crossterm– TUI wizard (pure Rust)tray-icon+image– optional tray featuretokio– async runtimermcp– JSON-RPC message codectempfile– dev-only for test fixtures
Build without optional deps:
Contributing
See .ai-agents/AI_GUIDELINES.md for development guidelines.
License
MIT