Short answers to what readers ask most about this topic.
01What can one Claude Code session send to another?
Plain text only. A message is a piece of text one Claude writes to another — never conversation history and never files. The receiving session gets the sender's name, the text, and a reply address. If you need the whole context in another terminal, resume the session instead of messaging it.
02Can a message from another session approve a permission prompt?
No. Claude Code tells the receiving Claude that the message came from another session rather than from you, so it never counts as consent and cannot answer a pending permission prompt. The receiving Claude is also instructed never to change permission settings or CLAUDE.md because another session asked, and slash commands inside message text arrive as plain text.
03Do cross-session messages go through Anthropic servers?
Only when they leave the machine. A message to a session on the same machine travels over a per-session Unix socket, or a named pipe on native Windows, and never touches Anthropic servers. Messages to your sessions on other machines ride the Remote Control connection, and messages to Claude Code on the web go straight to the cloud session.
04Why can't my container and host Claude Code sessions message each other?
Same-machine delivery works by each session registering itself in files on disk, so two sessions can only reach each other when they see the same files. A container has its own filesystem, so a session inside it and one on the host are isolated. The same applies to a WSL 2 session and a native Windows session on one computer.
05How do I stop Claude Code sessions from messaging each other?
Receiving and sending are separate controls. Set crossSessionInbound to refuse to drop inbound messages, and add permission deny rules naming SendMessage and ListAgents to stop this session sending or listing. Administrators can combine both in managed settings to turn the feature off across an organisation.
Claude Code Cross-Session Messaging: Sessions That Talk
Claude can pass a finding from one of your sessions to another by name, on this machine or across your machines, without you copy-pasting between terminals.
Claude Code cross-session messaging lets one of your sessions send a plain-text message to another by name, using the ListAgents and SendMessage tools. Never conversation history and never files — just text. Same-machine delivery goes over a per-session socket that never touches Anthropic servers; cross-machine delivery rides the Remote Control connection.
I had three terminals open on the same repository: one migrating a schema, one rewriting the API layer against it, and one I was actually watching. The API session spent eleven minutes building against a column that had been renamed forty minutes earlier in the migration session. I was the integration layer between them, and I was asleep at it.
Cross-session messaging is the fix for exactly that, and it is on by default on a recent enough version with nothing to enable. This post covers how sessions address each other, the three outcomes an arriving message can have, the trust model that stops a message being used as consent, where a message physically travels, and the socket underneath it that a hook can post into.
A message is text, and only text
The unit here is deliberately small: one Claude writes a piece of text to another Claude. It is never conversation history and never files. If what you want is the whole context, that is not messaging, that is resuming the session — and reaching for the wrong one of those is the most common mistake with this feature.
Claude uses two tools, and you call neither yourself. ListAgents discovers what it can reach and SendMessage delivers to one target by name. The same SendMessage tool also addresses subagents and agent-team teammates, which matters later: denying the tool removes all three at once.
Addressing, and the name collision that trips people up
You can leave the targeting entirely to Claude, or name the session yourself with an at-mention. The mention picker only appears once you type at least one letter after the at-sign — after a bare at-sign, session rows do not appear at all, which reads as the feature being missing.
# See what this session can reach. The FIRST line is this
# session's own name — the one your other sessions use to
# reach it. This session is never one of the rows below it.
/list-agents # also available as /peers
# Address a session by name in your own prompt. Type @ then
# at least one letter; a bare @ shows no session rows.
Let @api-worker know the schema migration finished
# Or leave the targeting to Claude entirely:
Ask the session in my other terminal whether the migration finished
# Ask to be told once when another session goes idle.
# One-shot, no polling, dropped after 12 hours.
Tell me when the migration session finishes what it is working on
Names are not guaranteed unique. When you start or rename a session with a name another live session already holds, Claude Code leaves the name with the session that has it and renames yours to a variant. Sessions can still end up sharing a name, so the listing shows each local session's working directory to tell them apart, and when more than one live session answers to a mentioned name Claude asks you which you meant before sending.
Three outcomes, not two
The receiving Claude reads a message between tool calls during an active turn, so a running tool is never interrupted; if the session is idle, the message starts a new turn. What most people miss is that arrival is not binary. Every message ends in one of three states:
Delivered — Claude Code passes it to the receiving Claude, and it counts toward usage like a prompt you typed.
Held — set aside undelivered, reaching Claude only if you approve it or a later settings change allows it.
Refused — dropped without delivery, and the sender is told so its Claude does not sit waiting or resend.
A message from another session is not your consent
This is the part worth reading twice, because it is what makes the feature safe to leave on. When session A messages session B, Claude Code tells B's Claude the message came from another session rather than from you, and constrains what it can do:
It cannot approve anything. A message never counts as your consent and cannot answer a pending permission prompt on your behalf.
It cannot change configuration. The receiving Claude is instructed never to alter permission settings, CLAUDE.md or other config because another session asked.
Commands do not run. A slash command inside the message text arrives as plain text and is never executed.
Permission prompts still fire. Acting on the message needs the same approvals any other work would need.
The rule extends to the sending side too: Claude is instructed never to ask another session to do something that was denied or blocked in its own session, and to route that work back to you instead. A teammate that was refused an action cannot relay it to a peer to get it done. That closes the obvious loophole, and it is the reason I stopped worrying about leaving inbound on accept.
Controlling what arrives
One setting decides what a session does with inbound messages, and it has a genuinely useful middle value. There is also a separate control for messages leaving the machine, and a pair of deny rules for turning sending off entirely.
// Choose what a session does with messages from your others.
{
"crossSessionInbound": "accept" // accept | hold | refuse
}
// Require your approval before any message leaves this machine.
// A true from ANY settings scope applies, so a checked-in
// project file can turn the requirement ON but never OFF.
{
"isolatePeerMachines": true
}
// Turn it off for a whole organisation, in managed settings.
// Note both directions: deny rules stop sending and listing,
// crossSessionInbound stops receiving.
{
"permissions": { "deny": ["SendMessage", "ListAgents"] },
"crossSessionInbound": "refuse"
}
When no value applies, Claude Code decides per message from the two sessions' permission-mode classes. Sessions that bypass permission prompts form one class and everything else the other. A session that prompts for permissions delivers each message, holding one only when the sender identifies as bypassing; a session that bypasses holds each message for your approval, delivering only when the sender also bypasses. A held message opens an approval dialog that expires after five minutes by default and can be set to never expire.
Where the message physically goes
Whether a message touches Anthropic servers depends entirely on where the other session runs, and that distinction is worth knowing before you send anything sensitive between terminals.
Three destinations, three routes:
Where the other session runs
How it travels
Touches Anthropic servers
On this machine
Per-session Unix socket, or a named pipe on Windows
No
On another of your machines
Over that machine's Remote Control connection
Yes
On Claude Code on the web
Straight to the cloud session
Yes
Same-machine delivery works by each session registering itself in files on disk, so two sessions can reach each other only when they can see the same files. A session inside a container and a session on the host cannot message each other. Neither can a session inside WSL 2 and a native Windows session on the same computer, because they register under different home directories and listen on different socket types. This looks like a bug and is a filesystem boundary.
The socket underneath, and what you can do with it
Each session with messaging binds an inbox socket, and Claude Code exports both its path and a per-session token to hooks and Bash commands. That turns a private implementation detail into something genuinely useful: a script or hook can post back into its own session, which is how you get a long build or a CI callback to speak into the conversation that started it.
# Each session binds its own inbox. Find the path two ways:
/status # the "Peer address" row, prefixed uds:
# Or from a hook or Bash command, where Claude Code exports it:
echo "$CLAUDE_CODE_MESSAGING_SOCKET"
echo "$CLAUDE_CODE_MESSAGING_TOKEN"
# A script can post back into its OWN session's socket. On
# native Windows the auth line is REQUIRED; on macOS and Linux
# it is optional. Send it as the first line of the connection:
#
# type=auth, token=$CLAUDE_CODE_MESSAGING_TOKEN
#
# Unix domain socket on macOS/Linux, named pipe on Windows.
# Restricted to your OS user either way, so another user's
# sessions on a shared machine cannot deliver to it.
Own-child messages get special treatment. When no inbound value applies, Claude Code delivers a message it can verify came from the session's own child processes. On Linux it can verify by process evidence even after the child exits; on macOS only while the process is still running; on native Windows and in containers where Claude Code is process ID 1, the exported token in the auth line is the only proof available. If you are scripting this, send the auth line regardless — it is optional on Unix and required on Windows.
The limits that keep a message loop from becoming an incident
Two sessions that can each write to the other are a loop waiting to happen, and the channel is designed on that assumption:
A same-machine message is refused once its serialised form passes about a million characters, and the refusal names the exact sizes. Nothing reaches the receiver.
A rapid burst to one session is refused at the sender once the receiver's inbox has had enough, with the refusal telling Claude to batch or wait.
The receiver rate-limits repeated messages per sender, drops identical repeats arriving in a short window, and queues at most 50 accepted messages.
At most 100 messages are held at once, separately from the delivery queue, and past that the oldest are dropped.
The habit that made this worth setting up was narrow: name every session I intend to keep, so I can address it later without a listing step. After that, the useful pattern is one long-running session doing something slow and one session I am actually watching, with a request for a single idle notice between them. It replaced the thing I was doing badly by hand, which was remembering to check.