Claude Lessons / Guides
Claude Code Cross-Session Messaging: Let Your Sessions Talk to Each Other
If you run more than one Claude Code session, you have been the message bus. One session renames a column; you alt-tab to the other terminal and re-explain it. One session finishes a migration; you keep checking whether it's done. Cross-session messaging removes you from that loop: Claude in one session can hand a finding, a status or a decision to Claude in another, on this machine or on a different one. There is nothing to install and no flag to set — if your version is recent enough, it is already on. This guide covers how a message actually travels, the inbound controls that decide whether it gets delivered at all, the limits that bite in practice, the socket underneath it that hooks can post into, and the decision most people get wrong: whether they wanted messaging or an agent team.
What cross-session messaging is — and is not
Cross-session messaging lets Claude in one of your sessions deliver a piece of text to Claude in another. That is the whole primitive, and the word text is doing a lot of work. A message is never the sender's conversation history, never its files, never its context window. It is one Claude writing a note to another Claude.
That boundary explains most of the feature's behaviour. If you want to move a whole conversation — the reasoning, the file reads, the accumulated context — you don't message it, you resume the session instead. Messaging is for the sideways handoff: the thing one session knows that another needs right now, mid-task.
The four cases it was built for:
- Hand over a finding. One session discovers a breaking change or makes a decision, and Claude summarizes it for the session working on the affected area — instead of you re-explaining it there.
- Coordinate parallel worktrees. When sessions work the same repository in separate git worktrees, Claude can tell the others what landed.
- Get status from long-running work. Have a migration or a test run report back to the session you're watching — or ask it yourself from there.
- Reach across machines. Message a session on another of your computers, or one running in the cloud.
Claude can decide to send a message on its own when it sees the need. It is not purely something you trigger — which is exactly why the inbound controls further down exist.
The 60-second version
Open two sessions. In the first, give it a name so the second can address it:
claude --name api-worker
In the second session, confirm it can see the first:
/list-agents
The first line of the output, when present, is this session's own name — the one your other sessions use to reach it. The rows below are what Claude can reach. Now just say what the other session should know. You are prompting Claude, not writing the message:
Let @api-worker know the schema migration finished
Claude writes the message itself and sends it with SendMessage. In the receiving terminal a
dim preview line appears in the conversation, something like
› Message from @api-worker: Schema migration finished (ctrl+o to expand). That's it.
You never call the tools yourself. ListAgents discovers the targets and
SendMessage delivers. Both are Claude's tools, not commands you type. The only things you
type are /list-agents (to see for yourself), /rename or --name
(to label a session), and your prompt.
Do you have it? Version and provider requirements
There is no "enable messaging" setting. A session either meets the requirements or it doesn't, and when it does, messaging is on.
| Capability | Minimum Claude Code version |
|---|---|
| Messaging at all — macOS, Linux, Linux inside WSL 2 | v2.1.224 |
| Messaging at all — native Windows | v2.1.234 |
| Starting a conversation with a session on another machine | v2.1.225 |
@-mentioning a session in your prompt |
v2.1.232 |
The /config row for inbound control |
v2.1.232 |
| Asking a session for a notice when it next goes idle | v2.1.236 (in both sessions) |
| Same-machine messaging on non-Anthropic providers, or with feature-flag fetching off | v2.1.248 |
Seeing undelivered messages under Waiting to be delivered in the transcript |
v2.1.309 |
Providers. Same-machine messaging works on every provider, including Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform and Microsoft Foundry — and in sessions running with feature-flag fetching off — from v2.1.248 onward. That is because same-machine delivery never touches Anthropic servers; it is a local socket. Reaching beyond this machine is the part that needs Remote Control and a claude.ai sign-in, so it is not available with an API key or on those providers.
How to check a session in one step. Type /list-agents (also available as
/peers). If the command isn't recognized, that session doesn't have the feature — start
with claude --version. If it works but a message you expected never arrived, messaging is
fine and something narrower applies: a deny rule, the receiving session's
inbound controls, or a target that isn't reachable. A session with messaging also
shows a Peer address row in /status.
Messaging vs. teams vs. subagents vs. resume vs. channels
This is the decision that matters, and it is the one third-party coverage skips. Claude Code now has
five ways to run work in parallel —
subagents, agent view,
agent teams, dynamic workflows and projects — plus three
supporting tools that aren't ways to run agents at all: worktrees, cross-session messaging, and the
/batch skill. Messaging sits in that second group. It is the wire, not the org chart.
| You want to… | Use | Why not messaging |
|---|---|---|
| Pass a finding between sessions you started and steer | Cross-session messaging | This is the case it exists for |
| Have Claude split a project, assign pieces and supervise workers | Agent teams | Messaging has no lead, no shared task list and no structured protocol — you hold the plan |
| Keep a noisy side task out of your main context window | Subagents | A subagent reports back to its own parent conversation; no peer wiring needed |
| Move a whole conversation and its context somewhere | Resume the session | A message carries text only — never history, files or context |
| Dispatch several independent tasks and check status at a glance | Agent view (claude agents) |
Messaging is complementary here, not a substitute — background sessions can be messaged too |
| Push external events (CI results, alerts, chat) into a running session | Channels | Messaging only carries text from your other Claude sessions, not from outside systems |
| Stop two parallel sessions from clobbering each other's files | Worktrees | Messaging coordinates information, not file access |
Three rules of thumb that resolve most real cases:
- Who holds the plan? If you do, messaging. If Claude should, an agent team. This is the single cleanest test.
- Does the worker need its own context, or its own files? Context → subagents. Files → worktrees. Neither is what messaging does.
- Is the other end even a Claude session? If it's CI, a monitor or a chat system, you want channels or an MCP server, not messaging.
Worth knowing: messaging and worktrees compose beautifully. Give each session its own worktree so they can't collide on files, then let them message each other so they don't duplicate work. That pairing is the practical answer to "how do I run three sessions on one repo without chaos", and it is strictly better than an agent team for that specific shape, because agent teams do not isolate teammates in worktrees — in a team you have to partition files by hand.
Sending: four ways to aim a message
You describe the outcome; Claude writes the text and picks the tool calls. There are four levels of precision available to you.
1. Describe the target by what it's doing
Claude discovers sessions with ListAgents, so a plain description is usually enough:
Ask the session running in my other terminal whether the migration finished
Note that this is a prompt you type, not the message Claude sends. Claude composes the actual wording, so leaving the content to Claude is fine and often better:
Explain what we just did to the session working on the payments API
2. Name the target with an @-mention
Type @ followed by the first letters of the session's name and pick it from the typeahead —
the same gesture as
@-mentioning a subagent. Claude Code inserts the
mention, such as @api-worker, and tells Claude which session it names, so Claude can send
without listing your sessions first. Requires v2.1.232 or later.
Two cases need more than the first few letters:
- A session beyond this machine appears in the typeahead only after Claude has listed or messaged your sessions beyond this machine — so ask Claude to list them first.
-
A name containing anything other than letters, digits, hyphens and underscores needs
double quotes:
@"release notes". Picking from the typeahead inserts the quotes for you.
You can type a mention without the picker too. If more than one live session answers to that name, Claude asks you which one you mean before sending.
3. Give sessions names worth mentioning
A session answers to the name you set with /rename or the --name flag. If you
set nothing, Claude Code names the session itself — which works, but makes your prompts vaguer than they
need to be. Naming sessions after the work (api-worker, migration,
frontend) is the single highest-leverage habit for this feature.
When several live sessions share a name, or Claude Code couldn't check everywhere your sessions run,
Claude adds a short identifier to each row of its listing and addresses the message with that identifier
instead of the bare name. Same-named sessions in different directories are also distinguishable in
/list-agents, which shows each local session's working directory.
4. Read the listing yourself with /list-agents
You don't need to run anything before asking Claude to send — but when something isn't working,
/list-agents is where you look. It groups what this session can reach:
- Subagents — agents running inside the current session.
- Teammates — this session's own agent team teammates.
- Your other local sessions — including background sessions. A session appears only when it binds an inbox socket.
- Your cloud sessions — shown while this session is connected to Remote Control.
-
Your Remote Control sessions on other machines — labelled
Remote Control, and shown asofflinewhen that machine's connection has dropped.
Remote Control withholds details. While this session is connected to Remote Control,
/list-agents leaves out each local session's working directory, leaves out any session
name it can't attribute to a person (that row reads (unnamed session)), and leaves out the
first line with this session's own name unless you typed that name at this terminal with
--name or /rename. The output ends with a note saying details were withheld.
None of this changes what Claude itself sees when it looks for a target — it is about what is shown to
whoever is reading the screen.
What arrives, and what the receiving session does with it
Timing. The receiving Claude reads the message between tool calls during an active turn, so a running tool is never interrupted. If the receiving session is idle, Claude Code starts a new turn with the message.
Appearance. Claude Code shows the message as a dim one-line preview in the conversation,
and that preview line stays there afterward. To see the full text: press Ctrl+O for the
transcript viewer, click a truncated preview line in fullscreen rendering, or run the session with
--verbose, which shows the full text instead of a preview. The preview shortens only what
you see — Claude always reads the whole message.
A real example, as the full text reads when you expand it:
Schema migration finished
The new column is tenant_id, and rebasing on main is safe now.
Mentions are not attachments. A message arrives as plain text. If it mentions a file or
an MCP resource with @, Claude sees the mention as written and Claude Code attaches
nothing — whether the message starts a new turn or lands mid-turn. Claude can still open a mentioned path
with its own tools on the receiving machine, subject to that session's permissions.
The four things an incoming message cannot do
This is the part to understand before you let sessions talk, and it is reassuring. Claude Code tells the receiving Claude that the message came from another session, not from you, and then limits it:
- It can't approve anything. A message from another session never counts as your consent, so it cannot answer a pending permission prompt on your behalf.
-
It can't change configuration. The receiving Claude is instructed never to change
permission settings,
CLAUDE.mdor other configuration because another session asked. -
Commands don't run. A slash command in the message text, such as
/compact, arrives as plain text. Claude Code never executes it. - Permission prompts still fire. If acting on the message needs a permission the receiving session doesn't have, you see the same prompt you'd see for any other work.
Permission boundaries stay per-session in the other direction too: Claude is instructed never to ask another session for an action that was denied or blocked in its own session, or that its own permission settings would block, and to route that work back to you instead. If you are writing permission policy for a team, that property is the one to quote — messaging is not a privilege-escalation path between sessions.
One cost note: once delivered, a message counts toward usage like a prompt you typed. Chatty sessions are not free.
The inbound controls that decide delivery
Every arriving message is checked by the receiving session, and the check ends in one of three outcomes:
- Delivered — Claude Code passes the message to the receiving Claude.
- Held — Claude Code sets it aside undelivered. A held message reaches Claude only when you approve it, or when a later mode or settings change allows it.
- Refused — Claude Code drops it without delivering.
You pick the policy with the crossSessionInbound setting:
| Value | Behaviour |
|---|---|
accept |
Claude Code delivers each message to Claude |
hold |
Claude Code shows a notice for each message and doesn't deliver it. If an accept
later applies, held messages are released
|
refuse |
Claude Code drops each message without delivering it |
Beyond editing a settings file you can select the value in the /config row
Messages from your other sessions, which writes it to your user settings. That row needs
v2.1.232 or later and doesn't appear while managed settings or the --settings flag set the
key, since a user-settings value wouldn't apply then. The /config crossSessionInbound=value
shorthand is rejected for this key — use the row or the file.
Precedence. Claude Code reads managed settings first, then the --settings
flag, then user settings, and applies the first value found. refuse is stricter than
hold, which is stricter than accept. When none of those trusted sources sets a
value, a project or local hold or refuse still applies — a checked-in project
file can tighten the policy but not loosen it.
A typo fails closed, loudly. Set crossSessionInbound to something
unrecognized — the classic being "reject" instead of "refuse" — and Claude
Code warns, ignores the value, and holds inbound messages for your approval while it is
present, even when a higher-precedence source sets a real value. In managed settings an unrecognized
value is treated as refuse outright. Before v2.1.248 the bad value was ignored silently,
so if you are on an older build, check the spelling by hand.
The default, when no value applies
This is the cleverest and least-documented part of the feature. With no crossSessionInbound
set, Claude Code decides per message from the two sessions' permission modes. It sorts every
session into two classes: those that
bypass permission prompts, and everything else. Plan mode
counts as bypassing in interactive terminal sessions where bypass permissions are available; auto,
acceptEdits and dontAsk all count as prompting.
| Receiving session | Sending session | Outcome |
|---|---|---|
| Prompts for permissions | Prompts for permissions | Delivered |
| Prompts for permissions | Bypasses prompts | Held for your approval |
| Bypasses prompts | Prompts for permissions | Held for your approval |
| Bypasses prompts | Bypasses prompts | Delivered |
Read that as a rule: a message crossing the bypass boundary in either direction gets held. A session that skips permission checks shouldn't be able to inject instructions into a careful one, and a careful session's text shouldn't silently steer one that will act on anything.
When the default holds a message in an interactive terminal session, an approval dialog opens there
showing the sender and a preview. Approve delivers that one message;
Deny, or dismissing the dialog, drops it. Leave it unanswered past the
dialogExpiry deadline — default 5m, settable to 60s,
10m or never — and Claude Code closes it and drops the message. For a
background session with no terminal attached, the dialog stays open past the deadline; after you attach,
a full deadline period of silence closes it.
If the session's permission-mode class changes while messages are held, Claude Code re-applies the inbound rules, delivers whatever they now accept, and shows a notice. Sessions in the VS Code extension or the Desktop app can't show the dialog at all, so a held message there waits out the same deadline.
Headless sessions. A claude -p session binds an inbox socket like an
interactive one, so a long-running worker can receive messages and appears in the listing — but it can't
show the approval dialog. A default-held message there is kept for the same dialogExpiry
deadline: if a mode or settings change allows it in time, it's delivered; past the deadline it's dropped
and reported as expired to a sender Claude Code can reach. Set dialogExpiry to
"never" to keep default-held messages until the session ends. To let a -p worker
take messages unattended, start it with crossSessionInbound set to accept in
its --settings value — an accept in your user settings works too, but applies
to every session you run. A message held by an explicit hold never expires; it is
delivered whenever an accept later applies.
Asking a session to tell you when it goes idle
The most immediately useful thing here is not messaging at all — it's the notice. Claude can ask one of your sessions on this machine to send back one notice when that session next goes idle or exits. Idle means it finished a turn with nothing queued. Requires v2.1.236 or later in both sessions.
You ask for it in plain language:
Tell me when the migration session finishes what it's working on
Claude subscribes using the SendMessage tool's notify_when_idle input — either
attached to a message it was sending anyway, or on its own. On its own, Claude Code subscribes
without starting a turn or spending tokens in the watched session, and sends the notice
immediately if that session is already idle. Attached to a message, the message is delivered first and
the notice follows later.
The watched session shows a line saying another process asked to be told when it is next idle. The asking session shows the notice as a line naming the watched session, and that line can include the time the turn finished plus a one-line status from it. If the asking session is idle when the notice arrives, Claude Code starts a new turn with it.
Four limits to know:
- 12 hours. If no notice arrives in that window, Claude Code drops the subscription and tells Claude, so it doesn't keep waiting.
-
refuseon either side means nothing arrives. The watched session drops the request without recording or answering it, so the subscription just expires unanswered after 12 hours. An asking session set torefusenever subscribes in the first place. -
holdon either side means you get less. A watched session onholdleaves the one-line status out; an asking session onholdshows the notice in your transcript without delivering it to Claude. - Main conversation, same machine only. Only the Claude in your main conversation can subscribe, and only to your sessions on this machine. Aim it at a teammate, a subagent or a session beyond this machine and Claude Code refuses the whole call — including any message attached to it.
Crossing machines and the cloud
Where the target runs changes how the message travels — and whether it passes through Anthropic servers at all. This table is the one to screenshot if you are answering a security question about this feature.
| Where the other session runs | How the message travels |
|---|---|
| On this machine | Over a per-session Unix socket on macOS and Linux, or a per-session named pipe on native Windows — never through Anthropic servers |
| On another of your machines | Through Anthropic servers, arriving over that machine's Remote Control connection |
| In the cloud | Through Anthropic servers, straight to the cloud session |
Starting a conversation with a session on another of your machines requires v2.1.225 or later and a target that actually appears in the listing. A row can carry a condition that changes what happens:
-
offline— that session's Remote Control connection dropped. The message goes through, but arrives only after its machine reconnects. -
can't receive cross-session messages (off in that session)— messaging isn't available there, or itscrossSessionInboundisrefuse. Claude Code refuses the send before the message leaves this machine, and the tool result beginsNot sentwith the reason. Fix the cause in that session and a later listing stops showing the condition.
Replies don't always work. A cloud session, including one on a self-hosted runner, can't
use SendMessage to answer a message from another machine even when the message carries a
reply address — the message still arrives, but you read the response in that session's transcript at
claude.ai/code. Similarly, if the sending session isn't connected to Remote Control when Claude sends
beyond this machine, the message goes through without a reply address, and the receiving Claude
can't answer it.
Container and WSL boundaries are hard walls. A session inside a container and a session on the host can't reach each other. Two sessions inside the same container can, including on a self-hosted runner. A session inside WSL 2 and a native Windows session on the same computer can't reach each other either. These are the most common "why can't it see my other session" causes after the version check.
Gating cross-machine sends. Set isolatePeerMachines to true to
require your explicit approval before any SendMessage reaches a session beyond this machine.
The prompt appears even in bypassPermissions mode, which skips ordinary permission
prompts — one of very few things that does. A true from any settings scope applies, so a
checked-in project file can turn the requirement on but not off, and same-machine messages never prompt.
{
"isolatePeerMachines": true
}
One more listing quirk worth knowing if a session seems missing: Claude Code reads the cloud and other-machine session lists newest first and stops after a bounded number of pages, so a session that fell past them can't be messaged by name.
The limits that bite in practice
These are properties of the channel itself and apply wherever messaging runs. Three of them produce errors you will eventually read.
| Limit | What happens |
|---|---|
| Plain text only | Structured agent team protocol messages stay inside a team; across sessions, text is all there is |
| Message size ≈ 1,048,576 characters serialized | Refused at the sender, naming both sizes. Nothing reaches the receiving session. Resending the same text fails identically |
| Rapid bursts to one session | Once a burst reaches what that session's inbox accepts, further sends are refused at the sender. The refusal tells Claude to batch the rest into one message or wait |
| Held messages: 100 | Past 100, Claude Code drops the oldest |
| Accepted queue: 50 | At most 50 accepted messages queue for Claude to read |
| Loop protection | The receiving session rate-limits repeated messages per sender and drops identical repeats arriving within a short window, so a two-session message loop stops on its own |
The errors, verbatim-ish, so you recognize them:
-
Message too large for cross-session delivery— the refusal names the serialized size and the limit, and tells Claude to put bulk content in a file the recipient can read rather than in the message. Before v2.1.235 an oversized message was reported as sent and silently dropped unread at the far end, which is a genuinely nasty failure mode if you are on an older build. -
Too many messages to this session just now— names the burst count and says the send did not happen. Before v2.1.236 these were also reported as sent and dropped unread. -
Refusing to send: reply target is a symlinkorRefusing to send: cannot vet reply target— before writing to a local session's inbox, Claude Code checks that the socket path is the endpoint the message was addressed to. A symbolic link sitting there could redirect the message somewhere the target session didn't create, so the send is refused. Usually you do nothing; if it repeats for one session, check what created a link at the path shown underPeer addressin that session's/status.
The practical habit: if you catch yourself prompting a session to relay a large file's contents, stop. Have it write a file and send the path. The receiving Claude can read it with its own tools — subject to its own permissions — and you stay far away from the size cap.
The inbox socket: hooks, scripts and sandboxes
Read this section for three reasons: a session you expected isn't in the agent list, you want a script or hook to post into a session, or a sandboxed command can't reach the socket.
Claude Code binds an inbox socket for each session with messaging enabled — a Unix domain socket on macOS and Linux including WSL 2, a named pipe on native Windows. Two places show you the path:
/statusshows it in thePeer addressrow, prefixed withuds:.-
Claude Code exports it to hooks and Bash commands as
CLAUDE_CODE_MESSAGING_SOCKET. In a session that starts with messaging on, the variable is exported before any hook runs, includingSessionStart.
Who can write to it. On macOS and Linux the socket is restricted to your operating-system
user. On native Windows every connection must first authenticate with a key only your OS user can read.
Either way, another user's sessions on a shared machine can't deliver to it. On macOS and Linux, Claude
Code also refuses to create the socket in a directory it can't accept — one another user owns, for
instance — and uses a private per-user directory, /tmp/cc-socks-<uid>, instead. When no
directory is acceptable the session runs without an inbox: you get a notice, /status shows
unavailable and the reason in Peer address, and the --debug log
records the full refusal.
Posting into your own session. Alongside the path, Claude Code exports a per-session
token as CLAUDE_CODE_MESSAGING_TOKEN. A script posting to its own session's socket sends
{"type":"auth","token":"<token>"} as the first line of the connection. The line is
optional on macOS and Linux including WSL 2 — connections are accepted with or without it — and
required on native Windows, where Claude Code closes any connection whose first line isn't a
valid auth line and delivers nothing from it.
Open the connection only when your text is ready. Claude Code closes a connection that hasn't sent a complete line within 30 seconds. So capture a slow command's output first, then open the connection and send it. Piping a long-running command straight into the socket is the mistake here.
Own-child messages. Socket arrivals go through the same inbound controls as any peer
message, with one exception: when no crossSessionInbound value applies, Claude Code delivers
a message it can verify came from the session's own child processes — a hook or Bash command posting back
to its own session. How it verifies varies by platform:
- On Linux, including WSL 2, it can verify by process evidence even for a child that has already exited.
- On macOS it can verify that way only while the posting process is still running; after it exits, it falls back to the token in the auth line.
- In a container where Claude Code runs as process ID 1 there is no process evidence at all, so the token is the only route. On native Windows the token is likewise the only way.
- When it can verify neither way, the message is treated like any other that asserts no permission class — so a session that bypasses permission prompts holds it for your approval.
The practical upshot: always send the auth line if your hook or script needs reliable delivery. It costs one line and removes the platform variance entirely.
Sandboxed sessions. Whether a Bash command can reach the socket from inside the sandbox
is controlled by the sandbox's Unix-socket settings,
sandbox.network.allowAllUnixSockets and sandbox.network.allowUnixSockets. If a
hook that posts to the socket works outside the sandbox and silently does nothing inside it, that is the
setting to look at.
Bare mode binds nothing. A session started in
bare mode doesn't bind the socket, so it can't receive
messages and doesn't appear in the agent list. Ordinary claude -p sessions do bind one.
Turning it off, for a session or an organization
Receiving and sending are separate controls, so turn off whichever direction you need — or both.
-
Stop receiving: set
crossSessionInboundtorefuse. From project or local settings that applies over every other source; from user settings it applies unless managed settings or the--settingsflag set a value. -
Stop sending and listing: add
permission deny rules naming
SendMessageandListAgents. Both take the bare tool name with no specifier.
For an organization-wide policy, administrators combine both in managed settings:
{
"permissions": {
"deny": ["SendMessage", "ListAgents"]
},
"crossSessionInbound": "refuse"
}
Two consequences that surprise people:
-
Denying
SendMessagealso kills subagent and teammate messaging, because the same tool serves all three. If you only meant to stop peer messaging, usecrossSessionInboundrather than the deny rule. -
A refusing session looks normal. Claude Code still binds its inbox socket and drops
everything that arrives on it. There is no visible change in its own
/statusor in other sessions' listings on the same machine — so to confirm the policy, read the settings files that apply to that session, not its status output.
Common mistakes
- Expecting the other session to inherit context. It won't. A message is text. If the receiving session needs to know why, the message has to say why.
-
Leaving every session unnamed. Then your prompts are guesses and the typeahead is
useless.
claude --name <thing>or/renametakes two seconds. -
Running one session in
bypassPermissionsand wondering why messages vanish. They're held, not lost — crossing the bypass boundary triggers an approval dialog, and that dialog expires in five minutes by default. - Pointing a hook at the socket without the auth line. It works on Linux, works on macOS while the process lives, and fails in containers and on Windows. Send the token.
- Using messaging where you wanted an agent team. If you find yourself manually relaying task assignments between three sessions, you are hand-rolling a lead. Use a team.
- Using an agent team where you wanted messaging plus worktrees. Teams don't isolate teammates in worktrees. If the risk is file collisions rather than coordination, worktrees plus messaging is the safer shape.
- Relaying bulk content. Write a file, send the path. The size cap is a megabyte and the failure is a refusal at the sender.
- Trusting a stale blog post. Plenty of third-party write-ups still say sessions can only reply across machines and never start a conversation, or that messaging is macOS and Linux only. Both stopped being true at v2.1.225 and v2.1.234 respectively.
Practice the multi-session habits, not just the flags
Claude Lessons has 38 free interactive lessons — naming and resuming sessions, running parallel work in worktrees, delegating to subagents, choosing a permission mode — practiced hands-on in a simulated Claude Code workspace with instant feedback.
Practice hands-on in the free interactive lessonsFAQ
How do I send a message from one Claude Code session to another?
You don't send it yourself — you ask Claude to. Tell the session what the other session needs to know, for example "let the session working on the payments API know the schema migration finished". Claude finds the target with the ListAgents tool and delivers with SendMessage. You never call either tool directly. To name the target explicitly, type @ followed by the first letters of the session's name and pick it from the typeahead, which requires Claude Code v2.1.232 or later.
Do I need to enable cross-session messaging in Claude Code?
No. There is no setting to switch on. Messaging is on in any session that meets the version requirement: Claude Code v2.1.224 or later on macOS, Linux and Linux inside WSL 2, or v2.1.234 or later on native Windows. To check a session, run /list-agents. If the command isn't recognized, that session doesn't have the feature and the first thing to check is claude --version.
Can another session approve a permission prompt or change my settings?
No. Claude Code tells the receiving Claude that the message came from another session rather than from you, so it never counts as your consent and can't answer a pending permission prompt. The receiving Claude is also instructed never to change permission settings, CLAUDE.md or other configuration because another session asked. A slash command inside the message text, such as /compact, arrives as plain text and is never executed. If acting on the message needs a permission the receiving session doesn't have, you get the same prompt you'd get for any other work.
Can Claude Code sessions message each other across machines?
Yes, with a condition. Same-machine messages travel over a per-session socket and never touch Anthropic servers. To reach a session on another of your machines or in the cloud, both this session and the target must be connected to Remote Control, which needs a claude.ai sign-in as the session's active authentication. Starting a conversation with a session on another machine requires Claude Code v2.1.225 or later. Claude can't find those sessions with an API key, or on Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform or Microsoft Foundry.
Why doesn't one of my sessions show up in /list-agents?
A session appears in the listing only when it binds an inbox socket. The usual causes are a session started in bare mode, which never binds one, a version below the requirement, or a directory Claude Code refused to create the socket in — check the Peer address row in that session's /status, which reads unavailable with a reason. Containers also matter: a session inside a container and a session on the host can't reach each other, and neither can a session inside WSL 2 and a native Windows session on the same computer. Cloud sessions and sessions on other machines appear only while this session is connected to Remote Control.
How do I stop my Claude Code sessions from messaging each other?
Receiving and sending are separate controls. To stop a session receiving, set crossSessionInbound to "refuse" and Claude Code drops inbound peer messages without delivering them. To stop a session sending or listing, add permission deny rules naming SendMessage and ListAgents, both written as the bare tool name with no specifier. Note that denying SendMessage also removes messaging to subagents and agent-team teammates, because the same tool serves all three.
Can a script or a hook post a message into a Claude Code session?
Yes. Claude Code exports each session's inbox address as CLAUDE_CODE_MESSAGING_SOCKET and a per-session token as CLAUDE_CODE_MESSAGING_TOKEN to hooks and Bash commands, and /status shows the address in its Peer address row prefixed with uds:. A script posting to its own session's socket can authenticate by sending {"type":"auth","token":"<token>"} as the first line of the connection — optional on macOS and Linux, required on native Windows. Open the connection only when the text is ready, because Claude Code closes a connection that hasn't sent a complete line within 30 seconds.
Cross-session messaging or agent teams — which should I use?
Use messaging when you are driving the sessions. You start each one, you decide what it works on, and Claude passes findings sideways when one session learns something another needs. Use an agent team when you want Claude to split a project up, assign the pieces and supervise the workers, with a shared task list and structured protocol messages. The practical difference is who holds the plan: with messaging you do, with a team the lead does. Messaging is also the only one of the two that reaches a session on another machine or in the cloud.
Running more than one session at a time?
Messaging is the wire between sessions. The lessons cover the rest of the multi-session toolkit — isolation, delegation, and keeping context under control — as hands-on practice rather than reference reading.
Start the free lessons