interclaude
by alexgoodell
README.md
# interclaude
**Your Claude Code sessions can talk to each other.** π€
Open two terminals, and one Claude can message the other β ask it a question,
hand off a task, coordinate a refactor across worktrees β with the reply
surfacing right in the conversation. No copy-pasting between windows, no
relaying through you. A peer's message just *appears*:
```text
<channel source="interclaude" sender="hazel" ts="2026-06-16T21:14:03Z">
tests are green on main β rebase your branch when ready, then send me the diffstat.
</channel>
```
Peer messages are actionable by default, so this isn't just chat β it's two (or
ten) agents genuinely collaborating. Split a big change across branches and let
the sessions sync up. Have one session run the slow test suite while another
keeps coding. Ask the session that wrote a module how it works. Fan a question
out to everyone with a broadcast.
## Quickstart
Two lines. The first installs the MCP server; the second installs the skill
that teaches Claude when and how to use it:
```bash
claude mcp add --scope user interclaude -- npx -y github:alexgoodell/interclaude
npx skills add alexgoodell/interclaude
```
Then launch **each** session you want connected with the development-channels
flag (this is what wires the inbound push β see [Requirements](#requirements)):
```bash
claude --dangerously-load-development-channels server:interclaude
```
That's the whole setup. There's no broker to start by hand and no config to
edit β each session auto-registers under a memorable alias (`hazel`, `otis`,
β¦). Ask any session to run `interclaude_list` to see who's reachable, then
`interclaude_send` to message a peer.
<details>
<summary>Alternatives: local clone, pinned alias, manual MCP config</summary>
`npx` re-resolves the repo on each session start. For a faster, offline-proof
launch, register a local clone instead:
```bash
git clone https://github.com/alexgoodell/interclaude && cd interclaude && npm install
claude mcp add --scope user interclaude -- node "$(pwd)/server.js"
```
Pin a specific alias instead of the auto-generated one:
```bash
INTERCLAUDE_ALIAS=alice claude --dangerously-load-development-channels server:interclaude
```
Register the server by editing `~/.claude.json` directly instead of using
`claude mcp add`:
```json
{
"mcpServers": {
"interclaude": {
"command": "node",
"args": ["/absolute/path/to/interclaude/server.js"]
}
}
}
```
Install the skill globally (all projects) rather than into the current one:
`npx skills add alexgoodell/interclaude -g`.
</details>
## Requirements
- **Node 20+**
- **Claude Code, launched with `--dangerously-load-development-channels
server:interclaude`.** This flag is mandatory, not optional: it's what lets
the server push a peer's message into your session as a `<channel>` block.
Without it you get the outbound tools but no inbound messages β the half that
makes interclaude worth using. Every participating session needs the flag.
## How it works
```text
session A session B
claude --dangerously-load-development-channels (same launch flag)
server:interclaude
β registers "proxy@feature-x" (alias "hazel") β registers "proxy@main" (alias "otis")
βΌ βΌ
server.js ββTCP/JSON :9877βββΆ broker.js βββTCP/JSONββ server.js
(per-session MCP adapter) (one detached (per-session MCP adapter)
β β² process, in-mem queues)
β βββ poll check() ~3s ββββββββββββ
βΌ
emit notifications/claude/channel
β <channel source="interclaude" sender="otis" β¦> appears in A's next turn
```
Three small files do the work:
- **`broker.js`** β a localhost TCP rendezvous (`:9877`) speaking line-delimited
JSON. Holds in-memory message queues keyed by session name, plus an alias map
and last-seen times. Auto-spawned detached the first time any session needs
it; if two sessions race to spawn it, the port bind picks a single winner.
Messages to a not-yet-registered recipient queue until it shows up, and each
message is delivered exactly once.
- **`server.js`** β the per-session MCP channel adapter. Registers a name
derived from the session's git worktree and branch, polls the broker every few
seconds, and pushes incoming messages into the conversation as
`notifications/claude/channel` events. Exposes the outbound tools below. If
the broker dies, the poll loop respawns it and re-establishes presence, so a
running session recovers on its own.
- **`client.js`** β the shared wire client (`request`), name derivation
(`deriveName`), and broker auto-spawn (`ensureBroker`), used by `server.js`
and the tests.
The bundled skill (`skills/interclaude/SKILL.md`, installed by the second
quickstart line) teaches an agent when to reach for these tools β and the one
rule that trips agents up: replies must go through `interclaude_send`, because
your transcript text never reaches a peer.
## Naming and addressing
- A session's canonical name is **`<worktree>@<branch>`** (e.g. `proxy@main`),
derived from its working directory at startup. Outside a git repo it falls
back to the directory basename. `INTERCLAUDE_NAME` overrides it.
- **Collisions disambiguate automatically.** A second session claiming a live
name is assigned `name-2`, `name-3`, β¦ and adopts the assigned name. A name
frees up ~10 seconds after its holder stops polling, so restarting a session
gets its old name back.
- **Every session auto-claims a memorable alias** (`hazel`, `otis`, β¦) from a
built-in name pool at startup, skipping names held by live peers. Canonical
names collide constantly β every session in one repo starts on the same
branch β so the alias is the primary handle, and outgoing messages are
labeled with it. Pin a specific alias with `INTERCLAUDE_ALIAS`, or replace
it at runtime with the `interclaude_alias` tool. A session holds one alias
at a time, and an alias expires with its session, so dead sessions don't
squat on good names.
## Tools
| Tool | Purpose |
|------|---------|
| `interclaude_send` | Send to a peer by name or alias (optional structured `data` payload) |
| `interclaude_broadcast` | Send to every other registered session |
| `interclaude_list` | Who's reachable (names, aliases, last-seen, live flag) |
| `interclaude_alias` | Claim/replace this session's short alias |
**Convention:** a message is a request to act unless it begins with `fyi:`,
which marks it informational β the receiving agent acknowledges but doesn't
act. This is a prompt convention in the server's instructions, not enforced by
the broker.
## Configuration
| Variable | Default | Meaning |
|----------|---------|---------|
| `INTERCLAUDE_PORT` | `9877` | Broker port (localhost only) |
| `INTERCLAUDE_NAME` | auto (`worktree@branch`) | Override this session's name |
| `INTERCLAUDE_ALIAS` | auto-generated | Pin this session's alias instead of auto-generating one |
| `INTERCLAUDE_POLL_INTERVAL` | `3000` | Inbound poll interval, ms |
## Broker lifecycle and troubleshooting
The broker is a single detached process that outlives any one session. Queues
are in-memory only: if the broker dies, undelivered messages are lost, but
every running session's poll loop respawns it and re-registers automatically β
no restart needed.
Check what the broker sees (run from this directory):
```bash
node -e "import('./client.js').then(async ({request}) => \
console.log(JSON.stringify(await request(9877, {action:'list'}), null, 2)))"
```
Force a clean slate (the next session poll respawns it):
```bash
pkill -f 'interclaude/broker.js'
```
Every poll re-asserts both the session's presence and its alias, so names and
aliases alike survive a broker restart without any manual re-claiming.
## Trust model
No authentication, by design β every participant is one of your own local
sessions on one machine, and any of them can drive another's tools through
channel messages. The broker binds to `127.0.0.1` only; do not expose the port
beyond localhost. If interclaude ever grows a remote transport or untrusted
peers, that is the moment to add pairing/allowlisting.
## Development
```bash
node --test
```
Unit tests cover the broker's routing/aliasing/disambiguation and the client
helpers. The channel push itself can't be unit-tested β it's verified
end-to-end by launching two real sessions and exchanging a message, which is
the completion bar for any change to `server.js`.
```text
interclaude/
broker.js # rendezvous: in-memory queues + alias map over TCP/JSON
client.js # wire client + name derivation + broker auto-spawn
server.js # per-session MCP channel adapter (inbound push + outbound tools)
test/ # unit tests (node:test)
skills/ # bundled agent skill (npx skills add)
```
## License
MIT
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues