Claude Relay
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Claude Relaysend to DESKTOP: I found the bug in auth.js line 42"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Claude Relay
Real-time communication between Claude Code instances across multiple machines via WebSocket + MCP.
What This Does
Enables Claude Code sessions on different machines to send messages to each other in real-time. Useful for:
Context sharing - Share findings, file contents, or investigation results between sessions
Task handoffs - Start a task on one machine, continue on another
Coordination - Let one Claude Code instance know what another is doing
Related MCP server: claude-session-bridge
Architecture
Machine A Machine B (Server Host)
┌─────────────────┐ ┌─────────────────┐
│ Claude Code │ │ Claude Code │
│ ↓ │ │ ↓ │
│ MCP Server │ │ MCP Server │
│ ↓ │ │ ↓ │
│ WebSocket ────┼── SSH Tunnel ─────┼─→ Relay Server │
│ (localhost) │ or direct │ (port 9999) │
└─────────────────┘ └─────────────────┘Components
Component | Description |
| WebSocket relay server (runs via launchd) |
| MCP server spawned by Claude Code instances |
| Session identity registry for human-readable IDs |
Installation
git clone https://github.com/gvorwaller/claude-relay.git
cd claude-relay
npm installQuick Start
1. Start the Relay Server (on one machine)
node server.js
# [Claude Relay] Ready! Listening on ws://localhost:99992. Configure Claude Code (on each machine)
Add to your Claude Code MCP configuration (~/.claude.json):
{
"mcpServers": {
"claude-relay": {
"type": "stdio",
"command": "node",
"args": ["/path/to/claude-relay/mcp-server.js"],
"env": {
"RELAY_URL": "ws://localhost:9999"
}
}
}
}3. Connect Remote Machines via SSH Tunnel
If machines aren't on the same network, use SSH port forwarding:
# On the remote machine, tunnel to the server host
ssh -N -L 9999:localhost:9999 server-host &
# Or use autossh for auto-reconnecting
autossh -M 0 -N -L 9999:localhost:9999 server-host &Session Identity System
Assign human-readable IDs to Claude sessions (CC-1, CC-2, CODEX, etc.) for easier coordination.
Setup Shell Aliases
Add to your ~/.zshrc or ~/.bashrc:
# Claude Relay Session Management
alias claude-session='source ~/claude-relay/sessions/register.sh'
alias claude-sessions='~/claude-relay/sessions/list.sh'Usage
Register a session (in terminal before starting Claude Code):
claude-session CC-1
# ✓ Registered: CLAUDE_RELAY_SESSION_ID=CC-1List all registered sessions:
claude-sessions
# === Registered Claude Sessions ===
# CC-1 PID: 12345 Started: 1/12/2026, 3:30:00 PM
# CWD: /Users/you/project
# CODEX PID: 67890 Started: 1/12/2026, 4:15:00 PM
# CWD: /Users/you/other-projectSession ID Priority
The MCP server determines client ID in this order:
CLAUDE_RELAY_SESSION_ID- Shell alias sets this--client-idcommand line argumentA single registry entry matching
RELAY_CLIENT_IDplus the current cwd, such asCODEX3for baseCODEXRELAY_CLIENT_IDenvironment variableAuto-generated:
hostname-pid
Session Registry
Sessions are tracked in ~/claude-relay/sessions/registry.json so all AI instances can see each other.
One view, live-verified. claude-sessions (sessions/status.js) is the single human-facing view: one table, one line per session, with state checked at print time — PROCESS (is the OS process actually running) and RELAY (does it have a live relay connection). claude-peers is the same table filtered to connected rows. Rows whose process is dead and that have no relay connection are pruned from the registry automatically whenever the table is printed, so ghosts clean themselves up. A row showing alive + NO RELAY means the agent is running but cannot send/receive relay messages (its relay MCP is not running or not connected) — that distinction was previously invisible.
Registry identity vs live peers
relay_sessions reads the registry, while direct message delivery uses the live WebSocket peer list. A session is healthy only when the same ID appears in both places.
The registry key, MCP CLIENT_ID, WebSocket clientId, and message from/to ID must be exactly the same. For example, a Codex window registered as CODEX3 must connect to the relay as CODEX3, not CODEX. If RELAY_CLIENT_ID=CODEX is configured and exactly one CODEXn registry entry matches the current cwd, the MCP server uses that exact registry ID. If a numbered registry ID is shadowed by a generic live peer, relay_sessions reports an identity warning instead of aliasing or rewriting delivery.
The relay server rejects duplicate live client IDs. Multiple Codex windows should therefore register distinct IDs (CODEX2, CODEX3, etc.) instead of sharing CODEX.
Wrong identity at startup? Startup resolution can pick the wrong ID when the spawning app (e.g. Codex) sets a fixed RELAY_CLIENT_ID and launches the MCP process from a cwd that matches no registry entry. No restart is needed to fix it: ask the session to call relay_rename with the correct ID (e.g. relay_rename to=CODEX1). The MCP client re-registers with the relay server under the new ID (the server drops the old identity from its live peer list immediately) and rewrites the local registry entry. If the target ID is live on another connection, the newest registration wins and the stale holder is displaced.
Displacement backoff. When a newer connection takes over this client's ID, the displaced client does not auto-reconnect — reconnecting under a taken-over ID guarantees an endless 5-second takeover ping-pong between the two holders (observed as thousands of duplicate_client_takeover events per day). It goes quiet instead; relay_status reports the DISPLACED state and the remedy: relay_rename to a new ID, or to the same ID to deliberately reclaim it.
Background forks don't inherit identity. Forked or background Claude sessions (--fork-session subagents, --bg-pty-host daemon resumes, scheduled runs) inherit CLAUDE_RELAY_SESSION_ID/RELAY_CLIENT_ID from the original session's environment. The MCP client detects that ancestry (or an explicit RELAY_BACKGROUND_FORK=1) and registers as <ID>-bg<pid36> instead of seizing the live session's identity. An explicit --client-id argument still wins — that's a deliberate choice by the spawner.
MCP Tools
Once configured, Claude Code will have these tools:
Tool | Description |
| Send a message to peer Claude Code instance(s) |
| Get recent messages from peers |
| Block for the next matching pushed message, with durable catch-up |
| List currently connected instances |
| Check connection health |
| Rename this session's live relay identity at runtime — no restart or env vars; the old ID is released immediately |
| List all registered sessions (including offline) |
| Remove all offline sessions from the local registry (online sessions kept; registry backed up first) |
| Clear the bounded memory cache; the durable journal remains intact |
| Delete durable history; restricted by |
Example Usage
Send a message:
Use relay_send to tell CC-2: "Found the bug - it's in auth.js line 42"Check for messages:
Use relay_receive to see if there are any messages from peersrelay_receive accepts optional from, to, and after filters. after may
be a returned message cursor or an ISO timestamp. Direct-message history is
visible only to its sender and recipient; broadcasts are visible to all peers.
Coordinate continuously with a peer:
Use the relay-coordinate skill to coordinate with CC2 until it sends RELAY_DONErelay_wait accepts an exact optional from peer ID, an optional after
cursor (message UUID or ISO timestamp), and timeoutSeconds from 1 through 300
(default 240). It first requests authorized durable history, then waits on the
existing WebSocket push path without polling the relay server. A returned
message includes its UUID cursor; pass that cursor as after on the next call.
Timeout and disconnect results do not advance the cursor.
The portable relay-coordinate skill loops
after normal timeouts, processes one peer request at a time, replies to the
exact peer, and stops on the exact RELAY_DONE token. Coordination remains an
intentionally active agent turn: it never interrupts running work and cannot
wake Claude Code or Codex after the session has returned control to the user.
Background doorbell for interactive Claude Code sessions
relay_wait intentionally holds its MCP tool call open. For an interactive
Claude Code session that should remain usable, start the content-free watcher
as a background Bash task instead:
node ~/claude-relay/scripts/relay-watch.js --for CC2 --timeout 240When a direct message to CC2 or a broadcast is durably stored, the helper
prints new-message and exits 0. A normal timeout prints timeout and exits 0;
connection failures exit 2. Run it with Claude Code's background-task support
so task completion re-enters the agent, then call relay_receive to fetch the
authorized content and cursor through the real MCP identity.
The watcher registers under a distinct generated ID and receives only a
doorbell payload (type, watched ID, and timestamp). It receives no sender,
content, cursor, or target history privileges. Like the relay itself, this is a
trusted-network/loopback tool and must not be exposed directly to the internet.
See who's online:
Use relay_peers to list connected instancesView all registered sessions:
Use relay_sessions to see all Claude sessions, online and offlineClear stale sessions (e.g., after a reboot):
Use relay_clear_sessions to remove all offline sessions from the local registryOnline sessions are never removed, and the registry is backed up to
sessions/backups/ before each clear.
Clear relay message history:
Use relay_clear_history to clear the in-memory cache while preserving the durable journalTo enable durable-history deletion, set a comma-separated admin allowlist in
the relay server environment, for example
RELAY_ADMIN_CLIENT_IDS=CODEX2,CC2, then use relay_purge_history from one of
those exact live client IDs. Without an allowlist, durable purge is disabled.
Message Retention and Logs
The relay appends every message to data/messages/YYYY-MM-DD.jsonl before it
routes the message. Files and their directory are owner-only (0600/0700).
The journal retains seven UTC days by default and is also capped at 100 MB;
the oldest files are removed first. On startup, the relay reloads a bounded
cache containing at most 500 messages or 10 MB.
Operational events are written as structured JSONL to
logs/operations-YYYY-MM-DD.jsonl. These records contain message IDs,
sender/recipient IDs, byte counts, and delivery status, but never message
content. Logs retain seven days, segment at 10 MB, and are capped at 50 MB.
Duplicate-client rejection records are rate-limited to one per client ID per
minute. The LaunchAgent sends stdout to /dev/null; stderr remains available
for failures that occur before structured logging initializes.
Defaults can be changed with:
Variable | Default |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| empty; purge disabled |
macOS Auto-Start (LaunchAgent)
Relay Server (on server host)
# Copy the LaunchAgent
cp com.claude-relay.plist ~/Library/LaunchAgents/
# Edit the plist to fix paths for your system:
# - Update /usr/local/bin/node to your node path (use `which node`)
# - Update /Users/yourname/claude-relay to your install path
# Load it
launchctl load ~/Library/LaunchAgents/com.claude-relay.plistVerify it's running:
launchctl list | grep claude-relay
# PID Status Label
# 1234 0 com.claude-relaySSH Tunnel (on remote machines)
# Install autossh
brew install autossh
# Copy and edit the tunnel LaunchAgent
cp com.claude-relay-tunnel.plist ~/Library/LaunchAgents/
# Edit to set your server hostname and paths
# Load it
launchctl load ~/Library/LaunchAgents/com.claude-relay-tunnel.plistTesting
Use the interactive test client:
# Terminal 1: Start server
node server.js
# Terminal 2: Connect as client A
node test-client.js MACHINE_A
# Terminal 3: Connect as client B
node test-client.js MACHINE_B
# In either client:
send Hello from here!
peers
historyConfiguration
Environment Variables
Variable | Default | Description |
|
| Port for relay server |
| (none) | Human-readable session ID |
|
| Relay server WebSocket URL |
Command Line Arguments
# Server
node server.js [port]
node server.js 8888
# MCP Server
node mcp-server.js --client-id=LAPTOP --relay-url=ws://192.168.1.100:9999File Structure
claude-relay/
├── server.js # WebSocket relay server
├── mcp-server.js # MCP protocol server for Claude Code
├── message-store.js # Seven-day JSONL journal and bounded cache
├── operational-logger.js # Rotated structured operational logs
├── test-client.js # Interactive test client
├── package.json # Node.js dependencies
├── sessions/
│ ├── register.sh # Shell script to register session ID
│ ├── list.sh # Shell script to list sessions
│ └── registry.json # Session registry (auto-generated)
├── logs/
│ ├── operations-*.jsonl # Rotated structured relay events
│ └── relay-error.log # Early startup/runtime stderr
├── data/messages/
│ └── YYYY-MM-DD.jsonl # Owner-only durable message journal
├── com.claude-relay.plist # macOS LaunchAgent for relay server
└── com.claude-relay-tunnel.plist # macOS LaunchAgent for SSH tunnelTroubleshooting
Connection refused:
Ensure relay server is running:
lsof -i :9999If using SSH tunnel, verify it's active:
ps aux | grep ssh
MCP tools not appearing:
Restart Claude Code after adding MCP config
Check MCP server is connecting: look for "Connected!" in logs
Messages not arriving:
Use
relay_peersto verify both instances are connectedCheck message history with
relay_receive
Orphaned MCP processes:
The MCP server includes a parent process watchdog
If Claude Code exits unexpectedly, MCP servers self-terminate within 10 seconds
To manually clean up:
pkill -f "claude-relay/mcp-server.js"
Session not showing correct ID:
Ensure you ran
claude-session CC-1BEFORE starting Claude CodeCheck with:
echo $CLAUDE_RELAY_SESSION_IDThe session ID is inherited from the shell environment
Security Notes
The relay server has no authentication by default
Designed for trusted local networks or SSH tunnels
All traffic over SSH tunnel is encrypted
Don't expose port 9999 to the internet without adding authentication
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityBmaintenanceEnables multiple Claude Code sessions to communicate and coordinate through broadcast and peer-to-peer messaging.21MIT
- Alicense-qualityAmaintenanceEnables multiple Claude Code sessions to communicate and share results automatically, with optional orchestration for hands-off workflow coordination.16MIT
- AlicenseAqualityBmaintenanceEnables Claude Code sessions to communicate with each other, allowing discovery, messaging, and synchronous queries across sessions.6MIT
- Alicense-qualityCmaintenanceEnables Claude Code instances to discover and communicate with each other across different sessions, supporting peer-to-peer messaging and coordination.24MIT
Related MCP Connectors
The team layer for AI coding agents: shared contracts, collision alerts, E2EE sessions.
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
Ephemeral REST chatrooms for AI agents to coordinate. Share a room URL — agents talk live.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/gvorwaller/claude-relay'
If you have feedback or need assistance with the MCP directory API, please join our Discord server