Cross-session messaging
Let Claude list your other Claude Code sessions and send them messages, locally, on other machines or in the cloud, with controls over what arrives.
Cross-session messaging lets one of your Claude Code sessions send a short text message to another. If a refactor in one terminal breaks something a second session is building on, Claude can warn it. If one session settles a question the other is stuck on, Claude can pass the answer across.
A message is just text that one Claude writes for another. It never carries the sender's conversation or files. To move a whole conversation, resume the session instead.
Note: Requires Claude Code v2.1.224 or later on macOS and Linux (including WSL 2) and v2.1.234 or later on native Windows. If a session meets the requirements, messaging is already on. See Availability.
When it helps
- Passing on a finding. One session discovers a breaking change and summarises it for the session working in that area, so you do not have to.
- Parallel worktrees. Sessions working the same repository in separate worktrees tell each other what has landed.
- Status from long jobs. A migration or test run reports back to the session you are watching, or you ask it from there. Locally, Claude can also ask for a single notice when the other session goes idle.
- Across machines. Reach a session on another computer or in the cloud.
Sending a message
Claude finds sessions with the ListAgents tool and sends with SendMessage. You never call these directly. Claude may send a message on its own when it spots a reason, or you can ask:
Check with the session in my other terminal whether the seed script has finished
You do not have to dictate the wording. Claude writes the message itself:
Tell the session on the reporting service what we changed in the date helpers
To name the target precisely, @-mention it (v2.1.232+). Type @ and the start of the session's name, pick it, and Claude Code inserts something like @billing-api and tells Claude which session that is:
Let @billing-api know the currency column is now a string
The typeahead lists your other live sessions on this machine. Sessions elsewhere appear only after Claude has listed or messaged them, so ask Claude to list them first. Names with spaces or unusual characters need quotes, like @"release notes"; the picker adds them for you. If several live sessions share the mentioned name, Claude asks which one you meant.
Delivery
The receiving Claude reads a message between tool calls, so a running tool is never interrupted. If the receiver is idle, a new turn starts with the message.
Messages are plain text. An @path or MCP resource mention inside a message is shown as written and nothing is attached, though the receiver can still open a path with its own tools and permissions.
The sender refuses to send when the message is over the size cap, when a burst to one local session has filled its inbox, when the target is listed as unable to receive, or when the reply target fails a safety check such as being a symlink.
On arrival, each message is delivered, held (set aside until you approve it or settings change) or refused (dropped), according to the receiver's inbound controls. A delivered message counts toward usage like a typed prompt, and the receiver can reply unless it was a one-way cross-machine message.
Permissions stay per session. Claude is told never to ask another session to do something its own session denied or would block, and to bring that back to you instead. The receiver's own permission prompts still apply to anything a message asks for.
Ask to be told when another session goes idle
Claude can ask a session on this machine to send one notice when it next finishes a turn with nothing queued, or exits. Both sessions need v2.1.236+.
Let me know when the data-backfill session is done with what it's doing
Claude uses SendMessage with its notify_when_idle input, either alone or attached to a message. Subscribing alone does not start a turn or cost tokens in the watched session, and if that session is already idle the notice comes straight back.
The watched session shows a line saying someone asked to be told. The asking session shows the notice naming the watched session, possibly with when its turn ended and a one-line status, and starts a turn if it was idle.
Limits:
- A subscription with no notice after 12 hours is dropped and Claude is told.
refuseon either side means nothing arrives.holdon either side means the notice arrives without the status line, and the asking side shows it to you without passing it to Claude.- Only Claude in your main conversation can subscribe, and only to local sessions. Asking a teammate, subagent or remote session for a notice refuses the whole call.
Seeing who Claude can reach
Run /list-agents (also /peers). The first line, when shown, is this session's own name. Below it:
- Subagents running in this session
- Teammates in this session's agent team
- Other local sessions, including background sessions, as long as they have bound an inbox socket
- Cloud sessions, while this session is connected to Remote Control
- Remote Control sessions on other machines, labelled
Remote Control, withofflinewhen their connection has dropped
While connected to Remote Control, /list-agents hides local working directories, hides session names it cannot attribute to a person ((unnamed session)), and hides this session's own name unless you typed it here with --name or /rename. Claude still sees everything when choosing a target.
A session answers to the name set with /rename or --name, or one Claude Code generated. If you pick a name another live local session already has, yours gets a variant. When one session answers to a name, Claude addresses it by name alone; when several do, or Claude Code could not check everywhere, Claude adds a short identifier.
Other machines and the cloud
| Target | Route |
|---|---|
| This machine | A per-session Unix socket (macOS, Linux) or named pipe (native Windows). Never via Anthropic |
| Another of your machines | Via Anthropic, arriving over that machine's Remote Control connection (v2.1.225+) |
| A cloud session | Via Anthropic, directly to the session |
Rows in the listing can carry two conditions:
offline: the message is accepted and delivered when that machine reconnects.can't receive cross-session messages (off in that session): messaging is unavailable there or it is set torefuse. The send never leaves this machine and the result beginsNot sent.
A session in a container cannot reach one on the host, and WSL 2 and native Windows sessions on the same PC cannot reach each other. Two sessions inside the same container can.
If this session is not connected to Remote Control when it messages a remote session, the message still goes but without a reply address, so it is one-way. Set isolatePeerMachines to require approval for anything leaving the machine (see below).
How the receiver treats a message
The receiving Claude is told the message came from another session, not from you, and:
- it cannot approve anything or answer a pending permission prompt
- it cannot change permission settings,
CLAUDE.mdor other configuration on request - commands inside it, such as
/compact, are just text and never run - any permission the work needs still prompts you
What you see
A delivered message appears as a dim one-line preview that stays in the conversation, for example:
› Message from @billing-api: Currency column is now a string (ctrl+o to expand)
Press Ctrl+O for the full text in the transcript viewer, click the preview in fullscreen mode, or start with --verbose to always see the full text. Claude always reads the whole message regardless, along with the sender's name and a reply address (absent for one-way remote messages).
Controlling inbound messages
The crossSessionInbound setting decides what happens to arriving messages:
| Value | Effect |
|---|---|
accept | Deliver to Claude |
hold | Show a notice but do not deliver. Released if accept later applies |
refuse | Drop silently |
You can also set it from the Messages from your other sessions row in /config (v2.1.232+), which writes to user settings. That row is hidden when managed settings or --settings set the key, and the /config crossSessionInbound=... shorthand is rejected. Precedence is covered in Settings reference.
The default when nothing is set
Claude Code compares permission-mode classes. Sessions that bypass permission prompts form one class; everything else (including auto, acceptEdits and dontAsk) forms the other. Plan mode counts as bypassing in interactive terminal sessions where bypass is available.
- Receiver prompts for permissions: deliver, unless the sender says it bypasses, in which case hold.
- Receiver bypasses: hold, unless the sender also bypasses, in which case deliver.
The asymmetry is deliberate: a session running without prompts should not take instructions from just anyone.
In an interactive terminal a held message opens an approval dialog with the sender and a preview. Approve delivers it, Deny or dismissing drops it, and it expires after the dialogExpiry deadline (five minutes by default). A background session with nobody attached keeps the dialog open until you attach, then starts the clock. If the session's permission class changes, held messages are re-evaluated. VS Code and Desktop sessions cannot show the dialog and keep held messages until the deadline. At most 100 messages are held; beyond that the oldest are dropped.
Headless sessions
claude -p sessions bind an inbox socket too, so long-running workers can receive messages. Bare mode sessions do not. A -p session cannot show the dialog, so default-held messages wait for dialogExpiry and are then dropped and reported as expired. Set dialogExpiry to "never" to hold them until the session ends. Messages held by an explicit hold never expire.
To let a -p worker accept messages unattended without changing every session:
claude -p --settings '{"crossSessionInbound":"accept"}' "watch the queue and process jobs"
The inbox socket
You need this when a session you expect is missing from the list, when a hook or script should post into a session, or when a sandboxed command cannot reach the socket.
/statusshows the path in itsPeer addressrow, prefixeduds:, orunavailablewith a reason.- Hooks and Bash commands get it as
CLAUDE_CODE_MESSAGING_SOCKET, exported before any hook runs,SessionStartincluded. - A per-session token is exported as
CLAUDE_CODE_MESSAGING_TOKEN. A script can open its connection with the line{"type":"auth","token":"<token>"}. That line is optional on macOS, Linux and WSL 2 and mandatory on native Windows.
The socket is limited to your OS user (on Windows, via the token). If Claude Code cannot accept the socket directory, it falls back to /tmp/cc-socks-<uid>, and if that fails too the session runs without an inbox and logs why under --debug. Send your line within 30 seconds of connecting or the connection is closed, so capture slow output before you connect.
Messages arriving on the socket pass through the same inbound controls, with one exception: when no crossSessionInbound applies, messages Claude Code can verify came from the session's own child processes (a hook or Bash command) are delivered. Verification uses process evidence on Linux, and on macOS only while the poster is still running; otherwise, and always on Windows or in containers where Claude Code is PID 1, it relies on the token. Unverified messages are treated like any other sender. Inside the sandbox, reaching the socket depends on sandbox.network.allowAllUnixSockets and sandbox.network.allowUnixSockets.
A typical use is a PostToolUse or Stop hook that posts a short status line back into its own session once a slow external check finishes. Because the hook is a child process of the session, the message is delivered even when no crossSessionInbound value is set, provided Claude Code can verify it as described above.
Restricting messaging
Approval for anything leaving the machine
{
"isolatePeerMachines": true
}
With this on, Claude Code asks you before any SendMessage reaches another machine or the cloud, even in bypassPermissions mode. A true from any scope applies, so a checked-in project file can switch it on but not off. Local messages are unaffected.
Turning it off
Sending and receiving are separate:
- Stop receiving: set
crossSessionInboundtorefuse. From project or local settings this overrides every other source; from user settings it applies unless managed settings or--settingssay otherwise. - Stop sending and listing: add deny rules for
SendMessageandListAgents(bare names, no specifier). See Permissions.
Organisation-wide, in managed settings:
{
"permissions": {
"deny": ["SendMessage", "ListAgents"]
},
"crossSessionInbound": "refuse"
}
The socket is still bound, but everything arriving is dropped. Denying SendMessage also removes messaging to subagents and teammates, since they use the same tool. A refusing session looks normal in /status and in other sessions' lists, so check the settings files to confirm.
Availability
- Operating systems: macOS, Linux, WSL 2 and native Windows, at the versions above.
- Local sessions: every provider, including Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform and Microsoft Foundry, and with feature-flag fetching off. Those cases need v2.1.248+.
- Remote and cloud sessions: only from a session connected to Remote Control, which needs a claude.ai sign-in as the active authentication. Not available with an API key or the third-party providers above.
To diagnose:
/list-agentsis not recognised: the session lacks the feature. Checkclaude --versionagainst the requirements.- It works but a message did not arrive: look for deny rules on
SendMessageorListAgents, the receiver's inbound settings, a missing Remote Control connection, anofflineorcan't receivelabel, or an old remote session that fell beyond the pages Claude Code reads.
Limitations
- Plain text only. Structured team protocol messages stay inside an agent team.
- Local messages over roughly a million serialised characters are refused at the sender.
- Rapid bursts to one local session are refused once its inbox is full; Claude is told to batch or wait.
- The receiver rate-limits each sender, drops identical repeats in a short window and queues at most 50 accepted messages, so loops between two sessions die out on their own.