Broker mode lets short-lived MCP client processes share a single long-lived upstream
xcrun mcpbridge session.
New here? Start with the Quickstart for a step-by-step walkthrough.
Two commands are all you need for the common case:
1. Start the broker daemon (once, in a terminal):
nohup uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
--broker-daemon --web-ui \
> "$HOME/.mcpbridge_wrapper/broker.log" 2>&1 &First-time only: Xcode shows an "Allow Connection?" dialog. Click Allow and wait for the reconnect loop to stop before connecting clients.
2. Configure every MCP client with --broker (they all attach to the running daemon):
uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
--broker --web-ui --web-ui-config "$HOME/.config/xcodemcpwrapper/webui.json"That's the entire recommended path. The daemon runs once; clients attach and detach as needed. See Client configuration examples for per-client config snippets.
# Confirm broker is running and version matches
mcpbridge-wrapper --broker-status
# Watch live broker events (Ctrl-C to exit)
tail -f "$HOME/.mcpbridge_wrapper/broker.log"
# Open the browser dashboard
open http://127.0.0.1:8080
# Or use the terminal UI (no browser required)
mcpbridge-wrapper --tui--broker-status prints daemon PID, version, and socket path. The dashboard and
--tui show live client sessions, tool call activity, and upstream status.
Use these three tools in order when anything looks wrong:
mcpbridge-wrapper --broker-statusReports whether the daemon is alive, its PID, and whether its version matches the installed wrapper. Version mismatch is the most common cause of unexpected behavior after an upgrade.
uvx --from mcpbridge-wrapper mcpbridge-wrapper --doctor--doctor connects to the running broker, probes the upstream bridge and dashboard,
and prints a structured pass/warn/fail report with suggested fixes. Run this as the
first step when any part of the stack looks unhealthy.
mcpbridge-wrapper --tuiOpens a live terminal view of the running daemon: upstream connection state, reconnect history, client sessions, and recent errors.
| Symptom | Recovery |
|---|---|
--broker-status shows "not running" |
Restart the daemon (see Quick setup) |
| Client shows 0 tools after Xcode approval | Reload/toggle the client; see Troubleshooting |
| Dashboard unreachable | --doctor → review Web UI findings |
| Version mismatch after upgrade | mcpbridge-wrapper --broker-stop, then restart daemon |
| Stale socket / PID file | mcpbridge-wrapper --broker-stop (or --broker auto-cleans on connect) |
mcpbridge-wrapper --broker-stop
# then restart with nohup ... --broker-daemon| Flag | Role |
|---|---|
| (none — default) | direct: each wrapper process launches its own upstream bridge. |
--broker-daemon |
Daemon host: long-lived process that owns the upstream bridge and accepts client connections on a Unix socket. Start this once, then point clients at it. |
--broker |
Proxy + auto-detect (recommended): connects to a running broker if one is alive, spawns a new daemon otherwise. Automatically recovers stale socket/PID files left by a crashed daemon. |
Use broker mode when you want lower process churn across repeated MCP client restarts.
Recommended topology for multiple agents/clients:
- Dedicated host frontend workflow (recommended when visibility matters):
run one explicit broker host with
--broker-daemon --web-ui, configure clients with--broker, and attach the browser dashboard and/or--tuito that same host. - Unified single-config auto-spawn: use the same client args everywhere:
--broker --web-ui --web-ui-config <shared-path>when you want the first client to own broker + dashboard startup implicitly.
Web UI behavior in broker modes:
--broker-daemon --web-uistarts broker + dashboard in one host process.--broker --web-uiforwards Web UI flags to the spawned daemon when auto-start is needed; if a broker is already running without--web-ui, a warning is printed to stderr.- When
--brokerreuses an already-running daemon, it does not change that daemon's dashboard state. --tuiis an operator frontend for an already-running dashboard; it does not start broker or Web UI ownership by itself.- Only one process can own a given Web UI
host:port. - If dashboard bind fails (for example port already in use), broker transport continues and only dashboard startup is skipped.
By default, broker state is stored in ~/.mcpbridge_wrapper/:
- Socket:
~/.mcpbridge_wrapper/broker.sock - PID file:
~/.mcpbridge_wrapper/broker.pid - Recommended log:
~/.mcpbridge_wrapper/broker.log
Start a dedicated background broker host first for predictable operation:
mkdir -p "$HOME/.mcpbridge_wrapper"
nohup mcpbridge-wrapper --broker-daemon --web-ui --web-ui-config "$HOME/.config/xcodemcpwrapper/webui.json" \
> "$HOME/.mcpbridge_wrapper/broker.log" 2>&1 &
echo "Broker started (PID $!)"Or using uvx:
nohup uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
--broker-daemon --web-ui --web-ui-config "$HOME/.config/xcodemcpwrapper/webui.json" \
> "$HOME/.mcpbridge_wrapper/broker.log" 2>&1 &Then configure MCP clients with --broker (see client examples below).
Prefer this pattern when you run more than one editor/client and want one clear daemon owner, one approval path, and one explicit place to inspect broker health.
Use it when:
- you want to start and stop the broker independently from editor restarts
- you want one known PID/log/socket identity to verify across multiple editors
- you want browser/TUI monitoring even when no editor is currently spawning the broker
- you are debugging reconnects, approval churn, or dashboard ownership
- Start one dedicated broker host:
nohup uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
--broker-daemon --web-ui --web-ui-config "$HOME/.config/xcodemcpwrapper/webui.json" \
> "$HOME/.mcpbridge_wrapper/broker.log" 2>&1 &- Attach one or both frontend surfaces to that same host:
# Browser dashboard (adjust host:port if your webui.json differs from defaults)
open http://127.0.0.1:8080
# Terminal UI (uses the same host/port/auth from the Web UI config)
mcpbridge-wrapper --tui --web-ui-config "$HOME/.config/xcodemcpwrapper/webui.json"-
Configure every editor/client with
--brokeronly. The clients attach to the running host instead of competing to own startup. -
Verify that multiple editors share one daemon:
# One daemon identity
mcpbridge-wrapper --broker-status
# One shared broker state directory
ls -l "$HOME/.mcpbridge_wrapper/broker.pid" \
"$HOME/.mcpbridge_wrapper/broker.sock" \
"$HOME/.mcpbridge_wrapper/broker.version"
# Recent broker events should settle after approval
tail -n 20 "$HOME/.mcpbridge_wrapper/broker.log"Then confirm the same daemon PID/state appears in the dashboard or TUI. With both editors connected, the frontend should show one shared daemon and live client sessions instead of separate host owners.
Important: Xcode may still show several mcpbridge-broker rows in Agent
Activity. Treat those rows as session/reconnect history, not as proof of
multiple live daemon hosts. Use --broker-status, broker files, and the shared
frontend state as the source of truth.
--broker is the recommended alternative that auto-detects: connects if a broker is alive, spawns otherwise (including dashboard args):
uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
--broker --web-ui --web-ui-config "$HOME/.config/xcodemcpwrapper/webui.json"mcpbridge-wrapper --broker-statusPrints proxy version, daemon PID, daemon version, file paths, and warns on version mismatch.
tail -f "$HOME/.mcpbridge_wrapper/broker.log"For day-to-day operator visibility, prefer the dashboard or --tui; the raw
log is most useful for reconnect details and startup diagnostics.
mcpbridge-wrapper --broker-stopSends SIGTERM to the running daemon and waits up to 3 seconds for a clean exit. If the daemon exits, PID/socket/version files are removed. If it does not exit in time, the command returns an error and preserves state files for manual recovery.
When upgrading mcpbridge-wrapper (via pip install, uvx, or ./scripts/install.sh):
- The install script automatically stops any running broker daemon.
- On next
--brokerlaunch, the proxy compares its version against the daemon's version file (~/.mcpbridge_wrapper/broker.version). If versions differ, the stale daemon is stopped and a fresh one is spawned automatically. - Use
--broker-statusto verify the running daemon matches the installed version.
If an older daemon was started before the upgrade and you want to force an immediate
restart, run mcpbridge-wrapper --broker-stop followed by any --broker command.
Use the same args in every client. The first client that needs auto-spawn starts the broker host and dashboard; later clients attach to the same host/session.
{
"mcpServers": {
"xcode-tools": {
"command": "uvx",
"args": [
"--from",
"mcpbridge-wrapper[webui]",
"mcpbridge-wrapper",
"--broker",
"--web-ui",
"--web-ui-config",
"/Users/YOUR_USERNAME/.config/xcodemcpwrapper/webui.json"
]
}
}
}{
"context_servers": {
"xcode-tools": {
"command": "uvx",
"args": [
"--from",
"mcpbridge-wrapper[webui]",
"mcpbridge-wrapper",
"--broker",
"--web-ui",
"--web-ui-config",
"/Users/YOUR_USERNAME/.config/xcodemcpwrapper/webui.json"
],
"env": {}
}
}
}claude mcp add --transport stdio xcode -- \
uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
--broker --web-ui --web-ui-config "$HOME/.config/xcodemcpwrapper/webui.json"codex mcp add xcode -- \
uvx --from 'mcpbridge-wrapper[webui]' mcpbridge-wrapper \
--broker --web-ui --web-ui-config "$HOME/.config/xcodemcpwrapper/webui.json"If you prefer explicit host lifecycle management, use the
dedicated host frontend workflow: start
one --broker-daemon --web-ui process manually and configure clients with
--broker.
- Back up your current MCP client configuration.
- Choose one rollout pattern:
- Dedicated host frontend workflow: start
--broker-daemon --web-uionce, attach dashboard/TUI explicitly, and set clients to--broker. - Unified config: set clients to
--broker --web-ui --web-ui-config <shared-path>.
- Dedicated host frontend workflow: start
- Restart each MCP client.
- Run a first MCP request and verify broker files exist:
~/.mcpbridge_wrapper/broker.pid~/.mcpbridge_wrapper/broker.sock
- If you are using the dedicated-host workflow, also verify that the dashboard
or TUI reports the same daemon PID you saw in
--broker-status. - Keep the same wrapper binary and package version across all clients that share the broker.
- Remove
--brokerfrom MCP config args. - Restart the MCP client.
- Stop any running broker process:
mcpbridge-wrapper --broker-stop- Verify direct mode behavior by running one tool call and confirming no broker files are recreated.
- Broker mode currently uses local Unix socket paths and is intended for single-user local workflows.
--brokerautomatically detects and removes stale socket/PID files left by a crashed daemon before spawning a new one.
The broker socket is protected by two complementary mechanisms so that only the same OS user can communicate with it:
-
File permissions — The socket file (
broker.sock) is created with0600permissions (owner read/write only) as soon as the daemon starts. Other OS users cannot even open a connection to the socket. -
Peer credential verification — Every accepted connection is verified using the operating system's peer credential API (
SO_PEERCREDon Linux,getpeereid()on macOS/BSD). If the connecting process's effective UID differs from the broker's own UID, the connection is rejected immediately with a JSON-RPC-32003error and closed without disturbing active sessions.
This is intentionally a local-user security model: the broker is designed for single-user workstations where all MCP clients run as the same macOS/Linux user account.
"Forbidden: UID mismatch" (code -32003) — The connecting process is running as a different OS user than the broker daemon. Ensure client and daemon are started under the same user account.
"Permission denied" connecting to broker socket — The socket file does not
have 0600 permissions or is owned by a different user. Check with
ls -la ~/.mcpbridge_wrapper/broker.sock. If the permissions are wrong, stop
the daemon and restart it so the socket is recreated with correct permissions.