claude-session-bus
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@claude-session-busbroadcast I'm rebasing the main branch"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
claude-session-bus
A tiny Claude Code plugin that lets separate Claude sessions working the same project pass each other short messages: "holding the dev server on :1420", "editing dynamics.rs", "branch X pushed, safe to rebase".
Send = an MCP tool (
broadcast) the calling session invokes.Receive = a
UserPromptSubmithook that prepends new peer messages to the other session's context before its next turn.Transport = append-only JSONL mailboxes, one per scope.
Broadcast-to-all, one-way, fire-and-forget. Advisory: it announces, it does not enforce. The sender picks how far each message travels - the same project, a group of projects, or every session on the machine.
Full design: docs/specs/2026-06-29-claude-session-bus-design.md.
Layout
claude-session-bus/
├── .claude-plugin/
│ ├── plugin.json # plugin manifest: hooks + mcpServers
│ └── marketplace.json # lets you add this dir as a local marketplace
├── server/index.mjs # MCP stdio server: broadcast(), inbox(), set_group()
├── scripts/drain.mjs # UserPromptSubmit hook (the receive half)
├── scripts/selftest.mjs # offline smoke test of the mailbox primitives
├── lib/bus.mjs # shared: boxes, group registry, identity, locking, message shape
├── hooks/hooks.json # declares the UserPromptSubmit hook
└── package.json # type:module, no runtime depsRelated MCP server: claude-intercom
Scopes
Every broadcast takes a scope. Each scope is a separate mailbox with its own
sequence numbers and per-session cursors, so the feeds never interfere:
scope | reaches | mailbox |
| sessions in this project, git worktrees included |
|
| sessions in every project that joined this project's group |
|
| every Claude Code session on this machine |
|
Git worktrees share the main checkout's box. A session in
<repo>/.claude/worktrees/feat-x resolves to <repo> via git rev-parse --git-common-dir, so it reaches the main checkout and every sibling worktree at plain
project scope, and its group membership is inherited rather than needing a set_group
call per throwaway directory. Working from a subdirectory of a repo resolves the same
way. A directory that is not a git repo keeps its own box. The per-message label still
comes from the directory the session actually works in, so worktrees are told apart by
their branch:
- 14:32 main#a1b2c3d4: pushed the release branch
- 14:33 feat/entry-field-visibility#9f8e7d6c [lock]: rebasing, hold off on app/Anything wider than project is tagged with its scope and the project it came from,
since the reader is sitting in a different repo:
- 14:32 master#a1b2c3d4: editing dynamics.rs
- 14:33 [group] my-app/feat-auth#9f8e7d6c [lock]: holding :1420
- 14:35 [global] infra-scripts/master#5a4b3c2d: rebooting the NASGroups are opt-in and named by you, one per project. The first time a session tries
to send at group scope from an ungrouped project, the tool refuses and tells the session
to ask you which group the project belongs to (offering the names that already exist);
it then calls set_group with your answer. Membership lives machine-wide in
~/.claude/.session-bus/projects.json - nothing about the bus is written into your repo.
you> tell the other repos the API contract changed
claude> broadcast(..., scope="group")
-> refused: no group set for C:/PROGRAMMING/my-app.
Existing groups: infra. Ask the user, then call set_group.
claude> This project isn't in a session-bus group yet. "infra" exists -
join it, or name a new one?
you> tauri-work
claude> set_group("tauri-work") -> joined. broadcast resent.Quick start
Clone the repo:
git clone https://github.com/exalsch/claude-session-bus.git cd claude-session-busSmoke-test the primitives (no Claude needed):
node scripts/selftest.mjs # expect: selftest OK - { ... }Install as a local plugin, then enable it:
/plugin marketplace add /path/to/claude-session-bus # the directory you just cloned /plugin install claude-session-bus@session-bus-devRestart Claude Code so the MCP server + hook load. Verify the
broadcastandinboxtools appear, and that thesession-busMCP server is connected.Use it. From any session:
broadcast("holding the dev server on :1420", kind="lock")- this project.broadcast("api contract changed", scope="group")- every project in this project's group (it will ask you to name one the first time).broadcast("rebooting the NAS", scope="global")- every session on the machine.inbox()to re-read peer messages on demand, across all three scopes.
Receiving sessions see the message prepended to their context the next time you submit a prompt there.
End-to-end test
Open two terminals, both cd into the same project (e.g. ~/projects/your-app):
In session A: ask it to
broadcast("test from A").In session B: submit any prompt. B should report a
[session-bus]line from A.In session A: submit any prompt. A should not see its own message (origin filter).
If A does see its own message, the ppid correlation did not hold (see Risks in the
spec) - the fallback is to render own posts tagged "(you)" instead of filtering them.
For the wider scopes, open a third terminal in a different project:
In session C: ask it to
broadcast("test from C", scope="global"). A and B should see it on their next prompt, tagged[global] <project>/<branch>.In session C: ask it to broadcast at
scope="group". It should stop and ask which group the project belongs to. Answer, then do the same in A. Once both projects name the same group, group messages flow between them and no further asking happens.
Config
SESSION_BUS_BACKFILL_MIN(default0) - minutes of backlog a session sees on its first prompt.0= forward-only (a new session only sees what arrives after it joins). Applies per scope.SESSION_BUS_RECEIVE(defaultproject,group,global) - which scopes reach this session. Set it toprojectto go back to v0.1 behaviour, orproject,groupto stay out of the machine-wide feed.SESSION_BUS_HOME(defaultCLAUDE_CONFIG_DIR, else~/.claude) - where the group and global mailboxes and the group registry live. Mainly for tests and for running two isolated buses side by side.Retention (
MAX_LINES,MAX_AGE_MS) lives inlib/bus.mjs.
Status & next steps
v0.2 adds the three scopes above. v0.1 behaviour is the default: a broadcast with no
scope still goes to the project mailbox and nothing else, and existing project
mailboxes and cursors keep working untouched.
The v0.1 headline risk - ppid correlation on Windows - was tested and did not
hold: Claude Code spawns the UserPromptSubmit hook through a shell, so the hook's
parent PID is a transient shell, never the directly-spawned MCP server's PID. Sessions
correlate on the session id instead.
CLAUDE_CODE_SESSION_ID alone turned out to be insufficient (fixed in v0.2.1). That
variable is frozen when a process is spawned, and the MCP server is long-lived: across
a resume or a plugin reload its copy goes stale, and a stale sender id means a session
no longer recognises its own posts and receives them back. Observed live - a server
stamping 292becbc… while its own hook half was c0430fa1…, with 292becbc…
belonging to no session at all. originId() now reads Claude Code's live process
registry at <claude-home>/sessions/<pid>.json first, keyed by the caller's parent
pid: the MCP server's parent is claude.exe, so it gets the current id, while the
hook's parent is a transient shell with no entry and keeps resolving by env var, which
for the hook is accurate. Both halves land on the same id, and the env var remains the
fallback. Covered by scripts/test-origin.mjs (unit) and scripts/test-drain.mjs
(integration); npm test runs the full suite.
Cost of the receive hook
The hook runs on every prompt and blocks it, under a 10s timeout, so its budget is the one number worth watching. v0.2.3 removed the last subprocess from that path.
Both halves used to shell out to git - rev-parse --git-common-dir to find the repo
family, rev-parse --abbrev-ref HEAD for the branch label. On Windows a process launch
is scanned, so those metadata lookups were priced like program launches. git writes all
of it to disk anyway (.git, the gitdir: pointer in a linked worktree, and
<gitdir>/commondir), so the bus reads the files directly. Measured on the dev machine:
before | after | |
resolve the project root | 229ms | 0.5ms |
in-process work, whole hook | 249ms | 13ms |
end to end, one hook | 318ms | 152ms |
end to end, 16 hooks at once | 910ms | 418ms |
What remains is node's own startup plus ~13ms of file IO, and nothing in the receive path
spawns a process - so the bus now also works where git is not on PATH at all
(scripts/test-nogit.mjs proves it by resolving with PATH stripped, checked against
real git's answers for a checkout, a subdirectory, a linked worktree, a bare repo and a
non-repo).
A timeout no longer costs you messages. Claude Code discards the output of a hook that overruns, and the hook used to advance its cursors before writing that output - so each timeout silently ate the peer messages it had just consumed. Cursors are now committed only after the payload is delivered, making the worst case a message shown twice rather than one lost.
Still out of scope: multiple groups per project, directed project-to-project addressing, group discovery beyond the registry, and any reply path.
This is dev tooling - it must never live inside a product repo.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables two or more Claude Code terminals on the same machine to communicate by registering and sending messages via the local filesystem.51MIT
- AlicenseNot gradedqualityDmaintenanceEnables real-time messaging between Claude Code instances, allowing agents to send, receive, and reply to messages instantly via file-based communication with auto-notification.2MIT
- AlicenseNot gradedqualityFmaintenanceEnables file-based agent-to-agent communication between Claude Code instances on the same machine, using MCP channels and plain JSON files.714MIT
- AlicenseAqualityCmaintenanceEnables communication between Claude Code sessions in iTerm2 panes, primarily for notifying other sessions when a PR is merged to main so they can pull latest changes.6MIT
Related MCP Connectors
StremAI MCP: shared memory for AI coding agents. Connected agents can recall. OAuth + local stdio.
The team layer for AI coding agents: shared contracts, collision alerts, E2EE sessions.
Read and write your Fresh Jots notes from Claude, Cursor, and any MCP client.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/exalsch/claude-session-bus'
If you have feedback or need assistance with the MCP directory API, please join our Discord server