Skip to content

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.
  • refuse on either side means nothing arrives. hold on 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, with offline when 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

TargetRoute
This machineA per-session Unix socket (macOS, Linux) or named pipe (native Windows). Never via Anthropic
Another of your machinesVia Anthropic, arriving over that machine's Remote Control connection (v2.1.225+)
A cloud sessionVia 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 to refuse. The send never leaves this machine and the result begins Not 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.md or 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:

ValueEffect
acceptDeliver to Claude
holdShow a notice but do not deliver. Released if accept later applies
refuseDrop 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.

  • /status shows the path in its Peer address row, prefixed uds:, or unavailable with a reason.
  • Hooks and Bash commands get it as CLAUDE_CODE_MESSAGING_SOCKET, exported before any hook runs, SessionStart included.
  • 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 crossSessionInbound to refuse. From project or local settings this overrides every other source; from user settings it applies unless managed settings or --settings say otherwise.
  • Stop sending and listing: add deny rules for SendMessage and ListAgents (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-agents is not recognised: the session lacks the feature. Check claude --version against the requirements.
  • It works but a message did not arrive: look for deny rules on SendMessage or ListAgents, the receiver's inbound settings, a missing Remote Control connection, an offline or can't receive label, 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.