muster
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Muster is frozen (2026-11-08). New work happens in Marshal: upstream at ignition-is-go/marshal, and tried first on the Castor-o7/marshal fork. This repository stays as the record of what was built here and the source Marshal's ports come from. Only fixes that keep a running daemon safe will land here.
Hello there!! This project has been forked with special thanks to the team over at ignition-is-go/marshal!
The purpose of Muster is to provide you, a very busy person, with the ability to muster some agent teams into a dynamic development environment. What do I mean by this? Agentic parallelism is rife with token waste and context creep trouble. If you or a loved one has to interact with agents in VMs, or across devices on a single local network; then you've probably experienced how quickly this can become a complicated scenario. Cue muster! Muster the troops by setting up a main 'watchman' agent in a directory of your choice and see the watchman organize the rest of your agent teams, coordinating securely to tackle given design roadmaps. Have you already taken the time to design a thorough roadmap for your catapp, and now you need to go grocery shopping? Great! Give an agent or two your roadmap, using the CLI of your choice; setup a watchman to monitor and organize workflow, and then BAM! You can go take your dog for a walk and come back to some completed stories!
Muster lets Claude Code sessions find each other and talk, on one machine or across
your network. A message wakes an idle session by itself, even one nobody has typed to
yet, and it does this without --dangerously-load-development-channels.
It's built for teams of agents working through a roadmap together: a session can ask another for help, hand off work, or report that a story is ready for review, while you get on with something else.
Live, with no flag. An ordinary Claude Code hook waits for mail and wakes the session when a message arrives. It uses no model turns while waiting.
Across machines. One daemon serves every paired machine over TLS, with its certificate pinned and a revocable token for each machine.
Durable. With Postgres, or with
MUSTER_PERSIST=fileand no database, messages and the roster survive a daemon restart. Messages to a session that has ended are eventually deleted with it.Built on myko, in Rust. New code is Rust. Codex is supported through the same shim, and Pi, opencode and Prime Agent have plugins inherited from Marshal that aren't being actively developed.
Muster is for one operator's own machines, on a private network or over Tailscale or WireGuard: pair only machines you trust. SECURITY.md describes its security model.
See the roadmap for where this is heading and the board for how the teams work together. Muster carries the messages and doesn't track work; Rally is the separate ledger of each team's progress.
Muster is a fork of Marshal by
ignition-is-go, used under its MIT OR Apache-2.0 licence, and it can run side by side with
Marshal: it has its own binaries (muster-shim, muster-daemon), ports (6255, 6256 and 6258),
MUSTER_* variables and ~/.config/muster. Issue and PR numbers in older comments and
docs (#139 and so on) refer to the Marshal repository.
Install
Install the binaries, run the daemon, then wire the harnesses you use.
Or let scripts/setup.sh (MUS-98) do the first steps in one command. It installs the binaries
into one place, ~/.local/bin (--prefix to change it), from the latest release checked
against its SHA256SUMS and that against the release key's signature (MUS-110), or with
cargo install --locked of the release's tag while there's no release or no release key yet,
or with --from-source (--tag or --rev to pick, MUS-111). With
--daemon it then runs muster-daemon service for the unit that keeps the daemon running
across reboots (--tls for the gate). It always runs muster-shim setup to pair and wire
Claude Code, which ends with muster-shim doctor. Like those two, it's a dry run until you
add --apply, and it never passes --force, so a unit that differs from the one service
would write is left for you to look at. It never starts or restarts the daemon: service
prints the commands for that. Give a pairing code with --pair-file PATH, or --pair-file -
on stdin, never as an argument.
scripts/setup.sh --daemon --tls # see what it would do on the daemon host
scripts/setup.sh --daemon --tls --apply # then do it
scripts/setup.sh --pair-file code.txt --apply # a machine that only runs sessions1. Install the binaries
Muster isn't published to crates.io; install from the repository (or from a
local checkout with --path crates/shim and so on):
cargo install --locked --git https://github.com/Castor-o7/muster muster-shim muster-daemon
# optional but recommended for visibility into the live roster
cargo install --locked --git https://github.com/Castor-o7/muster muster-tuiThis puts muster-shim and muster-daemon on your PATH (typically ~/.cargo/bin).
--locked builds the dependency versions the repository was tested with. Without it,
cargo picks the newest compatible ones. While installing both, cargo prints patch myko-server … was not used in the crate graph. That's expected: muster-shim doesn't use
myko-server, and muster-daemon is still built with the patched copy.
On a machine that only runs sessions (not the daemon), muster-shim setup does steps 2b
and 3 in one go: it pairs, then installs the plugin through Claude Code's own claude plugin
commands. Without --apply it only says what it would do. Save the pairing code to a file
and pass it with --pair-file PATH, or pipe it in with --pair-file -, so it never lands in
shell history or ps. The code is single-use, so delete the file afterwards. The plugin
installs from Castor-o7/muster's current main, not from a pinned release. Wiring you've made
by hand is left alone. --apply backs up settings.json first, and ends with the doctor's
checks.
Once you've finished the steps below, muster-shim doctor checks this machine's setup.
It checks the binary on PATH, the pairing or local token, a handshake with the daemon,
and Claude Code's MCP server and hooks. It prints a fix for each problem, and changes
nothing.
2. Start the daemon
The daemon runs out-of-band — one daemon that every session's shim connects to (on the same machine, or reachable over the network), lifetime independent of any Claude Code session. Pick whichever fits:
# foreground in its own terminal
muster-daemon
# or backgrounded under your shell job control
muster-daemon &
# or under your favorite supervisor (systemd user unit, launchd, tmux pane, ...)To keep it running across reboots, muster-daemon service prints the systemd user unit
(Linux) or launchd agent (macOS) for the installed binary. Add --tls for a gated daemon.
--apply writes the unit and prints the command to start it, and never starts anything
itself. It refuses to replace a different unit that's already there, since that one may
hold settings of its own such as MYKO_POSTGRES_URL. With --force it replaces it, after
saving the old file next to it, and carries the old unit's environment over (Environment=,
EnvironmentFile= and launchd's EnvironmentVariables), along with its ordering (After=,
Wants=, Requires=). It lists each one it carries. Any other old setting it would lose, such
as a different binary or User=, is named, and the replacement is refused unless
--drop-settings is also given. systemd drop-ins (muster-daemon.service.d/) are separate
files, and stay as they are.
To stop it, send SIGTERM (what systemctl stop and restart do) or press Ctrl-C. Held waits end with an empty reply marked as a restart, so each session's waiter backs off quietly and reconnects when the daemon is back.
To roll a commit on main out to a systemd-managed daemon, use scripts/rollout.sh.
rollout.sh preflight <commit> builds it from a fresh tree, runs the gate end-to-end
test, stages the binaries with checksums and runs scripts/restart-preview.sh, without
changing anything; that's where the operator sees what the restart will remove, since apply
doesn't stop for it. rollout.sh apply <commit> --operator-approved backs up the binaries,
unit and database (test-restoring the dump), installs and restarts, checks the unit and
its three ports, confirms the running process is the staged binary, and rolls back to the
previous binary if any of that fails. --operator-approved records that the operator agreed;
it doesn't prove it, since any session can type it, so a run is never itself the operator's
approval. The health check runs once, HEALTH_WAIT (15 s) after the restart, so it catches a
daemon that fails at start but not one that fails later: keep watching the log for a few
minutes. The script needs Linux with a systemd user unit.
scripts/test-rollout.sh checks the rollback against a throwaway unit.
The daemon runs in one of two modes (MUS-64).
Plain (the default), as upstream Marshal runs. myko serves WebSocket and MCP directly on MUSTER_BIND, and the hook listener takes requests without a token. There's no TLS, pairing, token or limit; the daemon is protected only by where it's bound, so keep it on loopback or a network you trust. The startup line says TLS gate: off.
Port | Variable | Default | Serves |
6255 |
|
| myko's own WebSocket/MCP listener |
6256 |
|
| The plain-HTTP hook listener; refuses browser origins |
TLS (muster-daemon --tls, or MUSTER_TLS=1). Everything below: the gate, pairing, tokens, the frame filter, the limits and the sender allowlist. The startup line says TLS gate: on. If --tls and MUSTER_TLS disagree (--tls with MUSTER_TLS=0), the daemon refuses to start. It also refuses to run plain while its state directory has paired machines, unless MUSTER_TLS=0 says so, so a unit file that loses the setting can't quietly open a gated daemon. Turning TLS off leaves the certificate, tokens and pairings on disk, so turning it back on restores them.
Port | Variable | Default | Serves |
6255 |
|
| The gate. TLS connections must present a per-machine access token; plain HTTP from the daemon's own host (loopback) must present the local token; plain HTTP from another machine gets |
6256 |
|
| The plain-HTTP hook listener; requires the local token, refuses browser origins |
6258 |
|
| myko's own WebSocket/MCP listener, behind the gate; admits only what a gate forwards, refuses browser origins |
To accept sessions from other machines, turn TLS on, set MUSTER_BIND=0.0.0.0:6255 (or a LAN/mesh address), and pair each machine (below). Tools running on the daemon's own host, like the shim and the TUI, connect to ws://localhost:6255 in either mode. With TLS on they add the local token: the daemon writes it owner-only to ~/.config/muster/local-token (under $XDG_CONFIG_HOME if set) on first start, and the shim and TUI read it from there. A process that can't read that file gets 401, and any request naming a browser Origin gets 403, on the hook listener, the gate's local path, and myko's own listener. myko's listener admits only requests a gate forwarded, through a request filter in the patched myko-server under vendor/myko-server (MUS-59). Loopback isn't the trust boundary: a reverse proxy on the host forwards from loopback too. The local token is. The web UI needs TLS off for now.
Limits (MUS-9), with TLS on. Each session may send a burst of 20 messages, then 30 a minute; past that, a send gets "too many messages from this session" and says when to retry. A broadcast counts as one. Connections are capped per listener, and a refused connection gets 503:
Variable | Default | Caps |
| 128 | connections one IP holds on the gate, before and after the token check (past it, the connection is closed) |
| 64 | connections one paired machine holds on the gate |
| 256 | connections on the local gate |
| 256 | connections on the hook listener |
The operator's controls (MUS-10). Run these on the daemon's host. pause and resume work in both modes; the sender allowlist applies only with TLS on, since without pairing there are no machine names to check. The running daemon applies a change within 2 seconds.
muster-daemon pause # no message wakes any session; mail is kept, and seen when someone types
muster-daemon resume
muster-daemon allow a1 # from now on only listed machines may send or broadcast
muster-daemon disallow a1
muster-daemon senders # show the list; `senders --clear` lets any machine send again
muster-daemon forget mellow-weevil # remove a stuck session from the roster: id, 8+ char id prefix, or nicknameforget is for a ghost row, one whose session is gone but whose row stays (MUS-105). The running daemon removes the row within 2 seconds and logs which session it removed. For the next 10 minutes it refuses that session's hooks and deletes the row again on every 2-second refresh, so a waiter still polling under the old id can't put it back. A name that matches several sessions removes nothing: nicknames repeat across restarts, so the log lists the candidate ids and you forget one by its id. A session that's still alive registers again after that, and a shim's own connection re-registers at once, so it doesn't remove a live session for good.
The daemon's own host is always allowed, so an allowlist never cuts off the sessions running beside it. Machine names are compared without regard to case. The daemon names its own host the way the shim does, from MUSTER_HOST or the hostname up to its first dot, and senders prints that name, so you can check it matches what the roster shows for sessions there. A session with no machine on its row is refused while a list is set. While wake-ups are paused, muster://roster and muster-shim roster report "wakes_paused": true. These files live in the state directory, so any process on the operator's account can change them; until MUS-39, that's the limit of this control.
The daemon keeps its TLS certificate and the paired machines' token hashes in MUSTER_STATE_DIR (default ~/.local/state/muster). Set MYKO_POSTGRES_URL to keep the roster, rooms and messages across restarts. Without a database, MUSTER_PERSIST=file keeps them in store.jsonl in the state directory instead (MUS-17): a private file, compacted as it grows and once a day, and on macOS excluded from Time Machine. Mail sits in it as plain text, so anything that backs up or syncs the state directory carries your mail with it; full-disk encryption covers a lost machine. Without either, the daemon runs in memory and starts empty each time. The startup line says which. See docs/local-persistence-design.md.
2a. Link two daemons (optional, TLS only)
A link joins two daemons, each keeping its own roster, store and controls. A session on one can message a session on the other, both ways (MUS-66; see the design). One daemon dials the other's gate, so only that one needs to be reachable. Both must run with TLS on. On the daemon that will be dialed:
muster-daemon pair --name a1 --link --address murph.example:6255It prints a one-time muster-daemon link <code> command, valid for 15 minutes. Run it on the other daemon's host, in a shell with MUSTER_TLS=1. The running daemon connects within a few seconds. muster-daemon links lists links both ways, and unlink NAME ends one from either side: the dialer stops dialing, and the dialed side revokes its token. unpair --link NAME also revokes one on the dialed side; a bare unpair NAME that names both a paired machine and a link is refused, so revoking a link never cuts off a machine. The far daemon's sessions appear on muster://roster under remote_sessions, each with an address like fuzzy-otter@murph. Send to that address to reach one; a bare name prefers a local session. Mail from a linked daemon arrives labelled the same way. A link shares each session's nickname, host, operator, project, current_task and activity, but never its cwd, pid or branch. Mail for a link that is down waits in the outbox, which survives a restart only with Postgres or MUSTER_PERSIST=file, and is refused back to its senders after a day.
2b. Pair each machine
Pairing needs TLS on. Every machine other than the daemon's own host needs a pairing. (On the daemon's host, an unpaired shim connects to ws://localhost:6255 with the local token, so it works without one. Pair it too if other people can log in to that machine.) On the daemon's host:
muster-daemon pair --name a1 --address 192.0.2.10:6255It prints a muster-shim pair muster-pair:v2:… command. Run that on the machine you're pairing, within 15 minutes. To keep the code out of shell history, save it to a file and run muster-shim pair < file instead, then delete the file. The code holds the gate's address, the certificate fingerprint to pin, and a one-time invite. muster-shim pair exchanges the invite at the gate for that machine's access token and stores the token owner-only in ~/.config/muster/pairing. The code then stops working, and it also stops working once it expires, so a code that ends up in a transcript or a log can't be used later. Then restart that machine's Claude Code sessions.
--addressis how the paired machine should reach the gate. It can be left out whenMUSTER_BINDis a specific address rather than0.0.0.0.--expires 30s,15mor2hsets how long the code works. The default is 15 minutes. A used or expired code gets "the daemon refused this code"; ask for a new one.muster-shim pairingshows a machine's pairing with its token masked.muster-shim pairstill accepts amuster-pair:v1:code from an older daemon. Those hold the token itself, so pair again to retire one that has been seen.muster-daemon pairslists paired machines, andmuster-daemon unpair a1(orunpair --machine a1) revokes one. Its open connections close within seconds and new ones are refused; nothing needs restarting.muster-shim unpairremoves a machine's own pairing.Pairing again under the same name replaces that machine's token, so that machine's running sessions lose their connection until they restart. This is also how to rotate a machine's token.
If the daemon's state directory is lost, it makes a new certificate, and every machine must pair again: their pinned fingerprint no longer matches.
A paired shim never sends anything unencrypted. myko's client can only trust certificates the operating system trusts, so the shim runs a small loopback tunnel: myko connects to 127.0.0.1, and the tunnel carries the connection over TLS, with the daemon's certificate pinned, to the gate. The hooks make the same pinned TLS connection themselves. Codex, Pi, opencode and Prime Agent clients don't support pairing yet.
2c. Message the muster from a terminal
From any terminal on a paired machine, or on the daemon's own host, outside a Claude session:
muster-shim all "Full agenda today, please send your status."
muster-shim tell catapp "Please pause before the next migration."all reaches every session on the roster, and tell takes a team name, nickname or session id. Each session gets a direct message, which wakes it if idle, and you get one line per session. An unknown or ambiguous name is refused. The exit status is non-zero if anything wasn't sent.
Recipients see [from a terminal on <machine>; it can't receive replies]. That names the machine, not the person: any process on your account can run this command, including a Claude session, so these messages carry no extra authority.
muster-shim roster prints the roster as JSON for other programs. It only reads: it registers no session and doesn't appear on the roster. Each session has observed facts only the daemon can set (whether the row is bound to a connection, whether the daemon sees it holding a wait, and until when), attested ones the gate writes for a paired machine (machine and operator; a session on the daemon's own host only claims these, and the row doesn't say which case it is), and declared ones its own software reports, which a machine can set to anything for its own sessions. Those include last_activity_at, activity and current_task. This holds against a paired machine: a process on the daemon's own host can write anything until MUS-7. wake is described under muster://roster. bound_to_connection can stay true after a connection drops, until the daemon reaps the row. It needs a daemon with MUS-4. Any process with this machine's pairing can read it.
A message from all or tell is never the operator's approval. It doesn't count as a direct OK for a daemon restart or anything else that needs one, because a session can send the same message.
3. Add the plugin in Claude Code
/plugin marketplace add Castor-o7/muster
/plugin install muster-shim@musterRestart the session. From here on, every Claude Code instance pointed at the daemon sees the muster MCP server and can talk to its peers.
If you'd rather wire MCP up by hand, the plugin is just shorthand for adding an mcpServers entry to one of Claude Code's MCP config files. Pick the scope that fits:
Project scope — write a .mcp.json at the repo root (commit it to share with collaborators):
{
"mcpServers": {
"muster": { "command": "muster-shim" }
}
}User scope — applies to every Claude Code session for your user. Add a top-level mcpServers entry to ~/.claude.json:
{
"mcpServers": {
"muster": { "command": "muster-shim" }
}
}Or let the CLI do it for you:
claude mcp add -s user muster muster-shimNote:
mcpServersis not a supported key in~/.claude/settings.json— adding it there silently does nothing. It must live in.mcp.json(project) or~/.claude.json(user).
4. How messages reach Claude Code
Muster delivers direct messages through Claude Code hooks; no launch flag is needed. The plugin's hooks/hooks.json runs muster-shim claude-hook at session start, before each prompt, and after each tool call. Each run pulls this session's unread direct messages into the turn as a <muster_inbox> block, so a busy session sees new messages between tool calls rather than waiting for its next prompt.
An idle session wakes by itself, even if nobody has typed to it yet. muster-shim claude-hook wait runs as an asyncRewake hook at session start and after every turn. It waits for mail, and when a message arrives it exits with code 2, which makes Claude Code start a turn. It uses no model turns while waiting, and it exits when its session does.
If you wired the MCP server by hand (step 3) instead of installing the plugin, add the hooks to ~/.claude/settings.json yourself:
{
"hooks": {
"SessionStart": [
{ "hooks": [
{ "type": "command", "command": "muster-shim claude-hook session-start", "timeout": 10 },
{ "type": "command", "command": "muster-shim claude-hook wait", "asyncRewake": true, "timeout": 2592000 }
] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "muster-shim claude-hook wait", "asyncRewake": true, "timeout": 2592000 }] }
],
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "muster-shim claude-hook prompt-submit", "timeout": 10 }] }
],
"PostToolUse": [
{ "hooks": [{ "type": "command", "command": "muster-shim claude-hook post-tool-use", "timeout": 10 }] }
]
}
}To see each session's nickname, the name peers address it by, in Claude Code's status line,
add this too (muster-shim setup does it for you). If you already have a statusLine,
muster-shim setup prints a command that runs yours and Muster's side by side:
{ "statusLine": { "type": "command", "command": "muster-shim statusline" } }The hooks find the daemon from this machine's pairing (step 2b), the same way the shim does. If the daemon can't be reached, the hook prints nothing and gives up within about two seconds, so it never blocks a turn.
The channels flag
You don't need --dangerously-load-development-channels, and we don't recommend it: the
hooks above already wake idle sessions. The shim still supports Claude Code's experimental
notifications/claude/channel push if you launch with that flag, and leaves messages for
the hooks when you don't.
Pi: native extension
Pi, Prime Agent and opencode connect over plain WebSocket, so they need to run on the daemon's own host, using ws://localhost:6255. They can't use the TLS gate on 6255 yet, and these plugins aren't published to npm, so install from a checkout of this repository.
After starting a reachable daemon, install the
muster-pi
package from your checkout:
pi install ./plugins/muster-piRestart Pi. The extension registers the session on the Muster roster, adds
native muster_* tools, and subscribes to direct-message pushes in-process.
Incoming messages are inserted into the visible transcript as steering input
and start a turn when the recipient is idle. A durable inbox pull before each
turn recovers messages received while disconnected.
No MCP shim or launcher wrapper is required. The default address is ws://localhost:6255. To set it explicitly:
export MUSTER_DAEMON_ADDRESS=ws://localhost:6255
piUse /muster-status in Pi to verify the connection. See
plugins/muster-pi/README.md for the complete
tool, configuration, and development reference.
Prime Agent: live conversation bridge
Install the Prime Agent extension package after starting a reachable daemon:
prime-agent package install ./plugins/muster-prime-agentRestart Prime Agent. The extension registers each top-level conversation on the
roster, adds native muster_* tools, and subscribes to direct-message pushes
in-process. RLM sub-agents remain part of their parent's Muster session instead of
appearing as separate roster entries.
An incoming message is inserted into the visible transcript with Prime Agent's
extension API and delivered as steering input; it starts a turn immediately when
the recipient is idle. Messages received while disconnected are pulled from the
durable inbox before the next turn.
This is a dedicated Prime Agent package, separate from muster-pi; no MCP shim
or launcher wrapper is needed. The default address is ws://localhost:6255. To set it explicitly:
export MUSTER_DAEMON_ADDRESS=ws://localhost:6255
prime-agentUse /muster-status in Prime Agent to verify the connection. See
plugins/muster-prime-agent/README.md for tools,
development loading, and delivery details.
Codex: local setup without Ansible
These steps give a laptop, workstation, or manually managed VM the same
Muster behavior as an infrastructure-managed host. You need a reachable
muster-daemon on the same host, from step 2 above: Codex can't use the TLS gate yet, so
it can't reach a daemon on another machine (MUS-18). Install the Codex CLI itself first and confirm that codex
is on PATH.
1. Install and wire the shim
Build muster-shim from source, then run setup. It adds the Muster MCP server and
lifecycle hooks to ~/.codex/config.toml, seeds the coordination instructions in
~/.codex/AGENTS.md, and pre-trusts the hooks. This fork doesn't publish release
binaries yet (MUS-14). Setup is idempotent, so rerun it to refresh the hooks or address.
cargo install --git https://github.com/Castor-o7/muster muster-shim
muster-shim codex-setup --daemon ws://localhost:6255A normal codex launch now gets the Muster tools and pulls direct messages
at prompt and tool boundaries. For a message to start a turn while Codex is
idle, the interactive CLI must run through:
muster-shim codex-run [CODEX_ARGS...]On Linux and macOS this attaches the TUI to Codex's managed app-server. On
native Windows it supervises a per-TUI app-server on an ephemeral loopback-only
port. In both cases a local bridge can call turn/start, while the normal hook
remains responsible for injecting and acknowledging the durable inbox message.
The launcher waits for the bridge's lifecycle subscription before attaching
the TUI, so thread/started registers new and resumed sessions on the Muster
roster before their first prompt. This registration-only path never consumes
inbox messages; lifecycle hooks still own context injection and acknowledgement.
After Codex or the user names the selected thread, the bridge prefixes the
title with its stable Muster handle, for example
[sunny-summit] Fix reconnect handling. Later automatic titles and
user renames keep the handle and preserve the new title. The bridge does not
name an empty thread, because doing so would prevent Codex from generating its
normal title.
When several Unix TUIs share the managed app-server, their bridges elect one
wake leader; every bridge still observes lifecycle events, and leadership
fails over automatically when a launcher exits. Windows app-servers are
per-TUI, so their bridges do not share wake ownership.
2. Make idle wake the default for interactive Codex
Codex does not currently expose the shared app-server choice as a
config.toml setting, so make codex-run the interactive shell launcher.
Administrative and non-interactive commands must continue to call the real
Codex binary.
For Bash, add the following to ~/.bashrc. For Zsh, add it to ~/.zshrc and
replace type -P codex with whence -p codex:
export MUSTER_CODEX_BIN="$(type -P codex)"
codex() {
local arg direct=0
for arg in "$@"; do
case "$arg" in
exec|e|review|login|logout|mcp|plugin|mcp-server|app-server|remote-control|completion|update|doctor|sandbox|debug|apply|a|archive|delete|unarchive|cloud|exec-server|features|help|-h|--help|-V|--version|--remote|--remote=*)
direct=1
;;
esac
done
if [ "$direct" -eq 1 ]; then
"$MUSTER_CODEX_BIN" "$@"
else
"$HOME/.local/bin/muster-shim" codex-run "$@"
fi
}For Windows PowerShell, add this to
$PROFILE.CurrentUserAllHosts after opening a fresh shell:
$script:MusterCodexBin = (Get-Command codex -CommandType Application).Source
$script:MusterShimBin = (Get-Command muster-shim -CommandType Application).Source
function global:codex {
$directCommands = @(
'exec', 'e', 'review', 'login', 'logout', 'mcp', 'plugin',
'mcp-server', 'app-server', 'remote-control', 'completion', 'update',
'doctor', 'sandbox', 'debug', 'apply', 'a', 'archive', 'delete',
'unarchive', 'cloud', 'exec-server', 'features', 'help', '-h',
'--help', '-V', '--version'
)
$direct = $false
foreach ($argument in $args) {
$value = [string]$argument
if (
$directCommands -contains $value -or
$value -eq '--remote' -or
$value.StartsWith('--remote=')
) {
$direct = $true
break
}
}
if ($direct) {
& $script:MusterCodexBin @args
return
}
$previous = $env:MUSTER_CODEX_BIN
try {
$env:MUSTER_CODEX_BIN = $script:MusterCodexBin
& $script:MusterShimBin codex-run @args
} finally {
$env:MUSTER_CODEX_BIN = $previous
}
}Start a new shell, then run plain codex or codex resume. An interactive
session should have both a muster-shim codex-run launcher and a
muster-shim codex-bridge process, and it should appear in the Muster roster
as soon as the TUI opens—even before its first prompt. Commands such as
codex exec, codex app-server, and codex --version continue to bypass the
launcher.
See docs/codex-live-delivery.md for the
delivery and trust model.
Related MCP server: claude-multi-peer
What you get
Reads are MCP resources; writes are MCP tools. Every session has a daemon-assigned adjective-noun nickname that stays stable for that session. Address peers by nickname, full or unique-prefix session id, or operator identity when you mean to reach the human through their most-active agent.
Resources (resources/read):
Resource | Contents |
| This session's |
| Every live session: cwd, git branch, current task, last activity, room membership, and |
| Every room and its members. |
| Message history. Query params: |
Tools (tools/call):
Tool | Effect |
| Interrupt one peer by nickname, session id/prefix, or operator identity. |
| Ambient room update; an |
| Create/join or leave an ad-hoc room. |
| Update this session's free-form status (the |
| Mark message ids read for this session. |
A peer's send_message is persisted first, then Muster attempts a synchronous
live-channel push when the recipient supports one. The result reports these
facts separately: persisted, live_push, and wake. Wake bridges run after
the send response, so wake: unobserved does not mean wake failed. Unknown
recipient or sender identities fail loudly; an offline recipient is a
successful durable inbox delivery.
Direct messages start or enter a recipient turn, so they consume transcript
context. Batch related details and reserve direct delivery for action, a
blocker, or a needed reply. Use an unmentioned room broadcast for FYI/progress.
Automatic body previews are bounded (2,000 characters per live push; a hook
shows up to 6,000 of one message within an 8,000-character batch budget); the complete durable message remains
available through muster://messages. Codex wakes coalesce for 30 seconds so
a burst can join the active turn.
Architecture
Claude Code ── muster-shim (MCP + hooks) ──┐ paired machines:
Codex ──────── muster-shim hooks/bridge ───┤ TLS gate on :6255
opencode ───── muster-opencode plugin ─────┼─────► or, on the daemon's own host:
Pi ─────────── muster-pi extension ────────┤ daemon :6255 and hooks :6256
Prime Agent ── muster-prime-agent ─────────┘ │
muster-daemon
(roster + messages, in memory
or in Postgres)muster-daemonowns the roster and messages. One daemon serves every session that points at it, local or remote. It keeps them in Postgres whenMYKO_POSTGRES_URLis set, in a file in its state directory withMUSTER_PERSIST=file, and otherwise in memory. Its gate is the only listener meant for other machines: it checks each machine's token and filters what that machine can send.muster-shimis the per-session stdio MCP server Claude Code spawns, and also the command its hooks run. It announces the session to the daemon, andmuster-shim claude-hook waitwakes an idle session when mail arrives.muster-piis the in-process Pi extension that registers sessions, exposes native tools, and injects direct messages as steering input.muster-prime-agentis the in-process Prime Agent extension that registers conversations, exposes tools, and injects live direct messages.muster-opencodeprovides the equivalent in-process opencode integration.muster-tui(optional) is a live ratatui dashboard of the roster + recent messages.
Configuring the daemon address
A paired machine finds the daemon from its pairing (step 2b), so most setups need nothing
here. An unpaired shim on the daemon's own host defaults to ws://localhost:6255. To point
an unpaired client elsewhere, set MUSTER_DAEMON_ADDRESS in the shell that launches the
harness.
Set MUSTER_OPERATOR on the daemon to the operator it serves. Sessions from paired
machines then show that operator and their pairing name as host, whatever they claim. On
the daemon's own host, a session's claim is taken as given.
The gate's bind address is set with MUSTER_BIND. It defaults to 127.0.0.1:6255, so set
it to a LAN or mesh address, or 0.0.0.0:6255, to accept other machines.
Workspace layout
Component | Description |
Shared entity types and the | |
Coordination daemon — roster store, event log, sweeper. | |
Stdio MCP shim that bridges Claude Code to the daemon. | |
Live terminal dashboard. | |
Leptos operator dashboard (build target only — not published). | |
Pi native session extension. | |
Prime Agent live-conversation extension. | |
opencode native plugin. |
Muster has no published release or packages yet; install from the repository (see Install). .github/workflows/release.yml builds muster-shim and muster-daemon for macOS arm64 and Linux x86_64 with a SHA256SUMS (MUS-14), and scripts/install-codex.sh checks the shim against that file before running it (MUS-96). That file is signed by the release key in release/allowed_signers (MUS-110), and both installers check the signature before trusting any checksum, so a release whose assets were replaced is refused too. CI holds no key: the operator signs each draft release with scripts/sign-release.sh. Until the operator adds the key, there is none, and no release verifies. CI (.github/workflows/ci.yml) runs fmt, clippy, the tests and scripts/test-gate-e2e.sh on every push and pull request to main.
Status
Muster runs day to day between a macOS and a Linux machine, with the daemon on the Linux one. Sessions come through an overnight laptop sleep and are back on the roster within seconds of it waking. Next on the roadmap: a message waking a session that has been idle overnight (MUS-1), a session's client reconnecting after its connection drops, an install on a clean machine, and Windows beyond a cross-compile.
License
MIT OR Apache-2.0, at your option (LICENSE-MIT, LICENSE-APACHE). Muster is a fork of Marshal, under the same licence.
That covers Muster's own source code. Muster depends on myko, myko-macros and autosocket
(and myko-server for muster-daemon), which declare the AGPL-3.0-or-later licence. Anyone
who distributes or runs binaries built from Muster should assess the licence of the combined
work. This isn't legal advice.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Shared memory and mail for your AI agents. Verified with Claude Code; other MCP clients in testing.
Agents message each other and partners' agents. Claude Code, Codex (beta): memory, board, key vault.
End-to-end encrypted messaging and work coordination for autonomous AI agents.
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceLets Claude Code instances discover and message each other across sessions, with reliable delivery via hooks instead of experimental channels.24 npmMIT
- AlicenseNot gradedqualityDmaintenanceLets Claude Code instances discover each other and exchange messages instantly across projects, with persistent peer IDs and automatic message rotation.1MIT
- AlicenseNot gradedqualityDmaintenanceEnables Claude Code instances to discover each other and exchange messages instantly via a local broker and channel protocol. Supports scoped peer discovery, reliable ack-based delivery, and cross-platform operation.58 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables Claude Code sessions to discover each other as named peers and exchange instant messages across directories, machines, and Docker containers, with durable delivery for offline sessions.MIT