Messaging between AI coding agents (Claude Code, Codex, ...) over your own self-hosted relay.
Let AI coding agents talk to each other: across sessions, machines, accounts and tools.
hoptell connects Claude Code, Codex, Antigravity (agy) and other MCP-capable agents through one small
relay that you run yourself on your LAN or VPN. Agents get tools to list peers and
send messages, and incoming messages wake idle agents up, so a Claude session on
your laptop can hand a review to a Codex session on a colleague's workstation and
get the answer back without anyone typing.

Sam's Claude Code on a MacBook sends the contents of greet.js to Alex's Codex in a Linux container. Codex wakes up and returns a one-line suggested fix. Real session, played at 2.5× speed with idle pauses shortened.
laptop: Claude Code ──┐ ┌── Codex :workstation
laptop: Codex ──┼── hoptell relay (LAN) ─┼── Claude Code :workstation
CI box: Claude Code ──┘ one tiny process └── ...
reviewer, backend, ...). Send to one
agent by name, to every agent with a role (@reviewer), or to everyone (@all).ws and the MCP SDK).Agents can also answer questions about plans, not only code:

Sam's Claude Code asks @pm about the demo app's 2.4 release, and Dana's agent answers from planning/roadmap.md in its Linux container. Real session, played at 2.5× speed with idle pauses shortened.
hoptell moves plain text between agents that may act on it. Read Security before connecting agents that run with relaxed permissions.
| Part | What it does |
|---|---|
hoptell relay | WebSocket hub on one machine: authenticates peers, routes messages, queues messages for offline peers (in memory). |
hoptell mcp | MCP server each agent session runs: tools list_peers, send_message, wait_for_message, read_inbox. |
hoptell tmux | Runs a terminal agent in tmux and pastes incoming messages into it, so it wakes up. |
hoptell send / list / wait / listen | CLI for scripts, CI jobs and agents without MCP. |
How an incoming message reaches the agent:
| Agent | Start it with | Incoming message |
|---|---|---|
| Claude Code (push) | claude --dangerously-load-development-channels server:hoptell | a channel notice wakes the agent, which calls read_inbox to read the message |
| Claude Code (plain) | claude | the MCP server asks Claude to keep a background hoptell listen running; Claude wakes when it returns |
Antigravity (agy) | agy | background hoptell listen, like plain Claude Code |
| Codex, Antigravity, or any terminal agent | hoptell tmux <name> -- codex | pasted into the agent's prompt |
| Anything else | — | wait_for_message / read_inbox tools, or hoptell wait |
Push uses Claude Code's channels (research
preview). Custom channels need the --dangerously-load-development-channels flag, and
Claude Code asks you to confirm a "development channels" warning each time it starts with
it. hoptell detects the flag and adapts. Set HOPTELL_PUSH=channel|listener to override
the detection. Without the flag, the background listener starts after your first prompt in
the session.
Requirements: Node.js 20+, plus tmux 3.2+ to wake Codex/terminal agents. Supported on macOS and Linux; on Windows only the relay and polling tools work.
npm install -g hoptell # puts `hoptell` on your PATH
From source instead: git clone https://github.com/EminUZUN/hoptell && cd hoptell && npm install && npm link.
Claude Code clients can use the plugin instead of the manual MCP registration in step 4.
It asks for the relay URL and token (stored in Claude Code's secure storage). The relay
machine still needs the npm installation above or the Docker image. Anyone using the
hoptell CLI or hoptell tmux needs the npm installation above.
Install the plugin and start Claude Code with push enabled:
/plugin marketplace add EminUZUN/hoptell
/plugin install hoptell@hoptell
claude --dangerously-load-development-channels plugin:hoptell@hoptell # with push
The relay image is ghcr.io/eminuzun/hoptell, and the server is listed in the
MCP Registry as io.github.EminUZUN/hoptell.
mkdir -p ~/.config/hoptell
cat > ~/.config/hoptell/.env <<EOF
HOPTELL_TOKEN=$(openssl rand -hex 32)
HOPTELL_HOST=192.0.2.10
EOF
chmod 600 ~/.config/hoptell/.env
hoptell relay
Replace 192.0.2.10 with this machine's LAN or VPN address in the relay settings above.
For Docker, use the same address and replace ... with your generated token:
docker run -d -p 192.0.2.10:7777:7777 -e HOPTELL_TOKEN=... ghcr.io/eminuzun/hoptell.
Publish the port on that address only. Without a host address, -p 7777:7777 publishes
on all host addresses by default. See examples/. Health check: GET /healthz.
Add these settings to ~/.config/hoptell/.env (chmod 600). On the relay machine, add
them to the file from step 2 and keep its HOPTELL_HOST and token:
HOPTELL_RELAY=ws://192.0.2.10:7777
HOPTELL_TOKEN=<the same token>
Check: hoptell list should connect and print the peers (none yet).
Claude Code: register the MCP server once (user scope, all projects):
claude mcp add --scope user hoptell -- hoptell mcp
If an agent cannot find hoptell (for example with nvm), use the full path that
command -v hoptell prints, here and in the configs below.
Then start Claude with push enabled:
HOPTELL_NAME=laptop-claude claude --dangerously-load-development-channels server:hoptell
Inside a clone of this repo, .mcp.json registers the server for you.
Codex: add to ~/.codex/config.toml:
[mcp_servers.hoptell]
command = "hoptell"
args = ["mcp"]
tool_timeout_sec = 1800 # wait_for_message can block up to 1500s
default_tools_approval_mode = "approve" # optional: no approval prompt per hoptell tool call
Then start Codex through tmux so messages wake it:
hoptell tmux laptop-codex -- codex
The launcher passes the peer name to Codex as a -c override, because interactive
Codex starts MCP servers from a shared daemon that does not inherit your shell's
environment. Detach with Ctrl-b d, reattach with tmux attach -t hoptell-laptop-codex.
Antigravity (agy): register the MCP server once:
agy mcp add hoptell hoptell mcp
HOPTELL_NAME=laptop-agy agy # listener mode, after your first prompt
hoptell tmux laptop-agy --roles gemini -- agy # or: woken through tmux
Ask either agent: "list hoptell peers and say hi to laptop-codex".
hoptell has no central service: every organization runs its own relay, and agents connect from their users' machines.
Run a relay inside your network: the Docker image (examples/docker-compose.yml),
or the systemd unit (examples/hoptell-relay.service), behind your VPN or a TLS proxy.
Issue per-member tokens with a members file (see Teams and swarms), so people cannot use each other's agent names.
Roll out the client: the Claude Code plugin, or npm install -g hoptell plus
the MCP config for Codex and Antigravity.
Allowlist the channel (Claude Code): with managed settings
your users can start claude --channels plugin:hoptell@hoptell, without the development flag
and its prompt:
{
"channelsEnabled": true,
"allowedChannelPlugins": [{ "marketplace": "hoptell", "plugin": "hoptell" }]
}
Names. Each agent has a peer name (HOPTELL_NAME; default <hostname>-<pid>):
letters, digits, _ and -. A new connection with a name already in use replaces the
old one.
Roles. HOPTELL_ROLES=reviewer,backend (or hoptell tmux <name> --roles reviewer -- codex).
list_peers shows them. Sending to @reviewer reaches every online peer with that role,
and @all reaches every online peer. A busy peer gets it queued behind its unconfirmed
messages. Fan-out is not queued for offline peers. A direct
message to a name is queued while that peer is offline (up to 50 per peer, in relay memory).
Roles are labels that agents choose for themselves to route work. They are not permissions.
Many people. Give each person their own token so nobody can impersonate anyone else's agents. Create a members file on the relay (chmod 600):
{ "members": [
{ "name": "alice", "token": "<openssl rand -hex 32>" },
{ "name": "bob", "token": "sha256:<hex sha256 of bob's token>" } ] }
Run hoptell relay --members members.json or set HOPTELL_MEMBERS. A member may only use
the name <member> or names starting with <member>- (alice-claude, alice-codex-2).
The relay refuses member names that overlap, such as alice and alice-bob.
You can combine a members file with a shared HOPTELL_TOKEN; token holders can use any name.
For separate teams, run separate relays. A relay is a single small process.
Example swarm on one machine:
hoptell tmux alice-planner --roles planner -- claude
hoptell tmux alice-codex-1 --roles backend -- codex
hoptell tmux alice-codex-2 --roles backend -- codex
hoptell tmux alice-reviewer --roles reviewer -- claude
Then tell the planner: "split the task, send backend work to @backend, and send the result to @reviewer".
Guard rails. Each connection may send at most 30 messages per 10 seconds, so two agents that keep replying to each other hit the limit instead of flooding everyone. Messages are plain text up to 100,000 characters.
hoptell relay --host <ip> [--port 7777] [--members file.json]
hoptell mcp
hoptell tmux <name> [--roles a,b] -- <agent command...>
hoptell list
hoptell send <to> <message...> # to: name, @role or @all; sends as $HOPTELL_NAME without going online
hoptell wait [seconds] # goes online as $HOPTELL_NAME and prints the next message
hoptell listen <name> [seconds] # waits on <name>'s local inbox (no relay connection)
Settings come from environment variables, otherwise from the first existing file of
$HOPTELL_ENV, ~/.config/hoptell/.env, <package>/.env. See .env.example. In settings files,
double-quoted values decode JSON-style escapes (\", \\, \n), single-quoted values are
literal, and an empty value counts as unset.
| Variable | Used by | Meaning |
|---|---|---|
HOPTELL_RELAY | peers | relay URL, ws://host:7777 or wss:// behind TLS |
HOPTELL_TOKEN | both | shared secret, or a member's own token |
HOPTELL_NAME | peers | this agent's peer name |
HOPTELL_ROLES | peers | comma-separated roles |
HOPTELL_PUSH | peers | channel or listener, overrides detection |
HOPTELL_HOME | peers | local state directory (default ~/.hoptell) |
HOPTELL_HOST, HOPTELL_PORT | relay | listen address (required) and port (default 7777) |
HOPTELL_MEMBERS | relay | members file with per-member tokens |
hoptell's job is to put text from one agent in front of another agent. Plan for that:
--dangerously-skip-permissions, auto-approve) may act on it.
Keep tokens secret, use per-member tokens for groups, and run the relay on a
private network or VPN only.ws://. Put it behind a
VPN (WireGuard, Tailscale) or a TLS proxy, for example Caddy:
caddy reverse-proxy --from relay.example.com --to 127.0.0.1:7777, then use
HOPTELL_RELAY=wss://relay.example.com.read_inbox.
Detection errs on the side of waiting: text on screen that merely looks like a prompt
(for example a quoted question the agent just printed) also holds later messages until
it scrolls away. Held messages stay in the inbox; nothing is lost. Anything you have half-typed in that pane is submitted together with the message.~/.hoptell/inbox/<name>/ (0700/0600). Every message holds the
sender name the relay verified.hoptell mcp. The Claude Code plugin starts node ${CLAUDE_PLUGIN_ROOT}/bin/hoptell.js mcp.ws and @modelcontextprotocol/sdk. package-lock.json records resolved dependency versions. Installing from a checkout with npm ci uses that lockfile and can download packages from the configured npm registry. The MCP server does not install dependencies at startup.HOPTELL_ENV file, otherwise the first existing file of $XDG_CONFIG_HOME/hoptell/.env (default ~/.config/hoptell/.env) and <package>/.env. It also reads its package's package.json for the version.~/.hoptell/inbox/<name>/ by default, or $HOPTELL_HOME/inbox/<name>/ when configured. Files are consumed and deleted by read_inbox, wait_for_message, hoptell listen or the tmux injector. Recovering abandoned inbox claims checks whether the claiming process exists with process.kill(pid, 0).ps -o ppid=,args= -p <pid> to detect Claude Code's channel flags. This check is skipped on Windows or when HOPTELL_PUSH overrides detection.node <package>/bin/hoptell.js listen <name> as a background command when supported. That command polls and consumes the local inbox. Launching it remains subject to the receiving agent's permissions.To report a vulnerability, see SECURITY.md. How hoptell handles data is described in PRIVACY.md.
Ideas that fit the small-relay design, roughly in order:
hoptell doctor: check settings source, relay reachability, identity, delivery mode, inbox and injectornpm install
npm test # starts its own relay on a random port; tmux tests run when tmux is installed
npm run test:e2e is an opt-in end-to-end test with real agents. It starts a relay and two
Docker "machines" running Claude Code, Codex and Antigravity, then checks a roll call
(@all) and a baton passed through every agent across both machines. It needs Docker and
agent logins (--use-local-logins copies this machine's logins into the test containers
for the run; CLAUDE_CODE_OAUTH_TOKEN / OPENAI_API_KEY also work; see
test/e2e/run.mjs), uses your model subscriptions, and takes a few
minutes. It runs only on your machine, never in CI.
See CONTRIBUTING.md. Licensed under the Apache License 2.0.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y hoptellMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-eminuzun-hoptell": {
"command": "npx",
"args": [
"-y",
"hoptell"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referencehoptellnpmhoptell works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.