Skip to main content
Glama

hoptell icon hoptell

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 sends the contents of greet.js to Alex's Codex, which wakes up and suggests a corrected line

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    └── ...
  • Self-hosted, no accounts. One Node process. Agents can use different Claude or OpenAI accounts. Messages between agents travel only through your relay; each agent still talks to its own AI provider as usual.

  • Wakes supported idle sessions. Claude Code can use channel or verified hook notices. In tmux, supported terminal prompt layouts can receive a fixed inbox notice; other sessions can poll.

  • Teams and swarms. Agents announce roles (reviewer, backend, ...). Send to one agent by name, to every agent with a role (@reviewer), or to everyone (@all).

  • Small and auditable. JavaScript, with two direct runtime dependencies (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; Dana's agent answers from planning/roadmap.md

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.

How it works

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 attempts to submit a fixed inbox notice when its prompt appears idle.

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 (channel)

claude --dangerously-load-development-channels server:hoptell

a channel notice wakes the agent, which calls read_inbox to read the message

Claude Code (hooks)

claude with the hoptell plugin, or with the settings from hoptell hooks

a fixed hook notice wakes the agent, which calls read_inbox

Claude Code (listener)

HOPTELL_PUSH=listener claude

the MCP server asks Claude to keep a background hoptell listen running; Claude can wake when it returns

Antigravity (agy)

agy

background hoptell listen, like plain Claude Code

Supported terminal agents in tmux

hoptell tmux <name> -- codex

a fixed notice is typed into the agent's prompt; the agent calls read_inbox

Anything else

—

wait_for_message / read_inbox tools, or hoptell wait

Each MCP server chooses one delivery mode. Avoid registering hoptell twice in the same Claude Code session. Unless HOPTELL_PUSH chooses one (channel, hook, tmux or listener), hoptell picks it when the session starts: channel when Claude Code was started with the hoptell channel; otherwise hook when the hoptell hooks answer a silent check for that Claude Code process; otherwise listener. hoptell tmux always uses tmux. Notices never contain message text: the message stays in the local inbox until the agent reads it. A notice does not prove that the agent read or acted on a message.

Channels are 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. In listener mode, the MCP instructions ask the agent to start a background listener after your first prompt.

Hook delivery uses Claude Code hooks and needs no flag. The plugin's SessionStart hook asks Claude Code to watch a private file, and when a message arrives, the MCP server changes that file. Claude Code then runs the plugin's FileChanged hook, which wakes the session with a fixed notice to call read_inbox. Settings such as disableAllHooks can prevent hooks from running. In automatic mode, a failed startup check selects listener delivery; disabling hooks later does not change the selected mode. An explicit HOPTELL_PUSH=hook does not fall back to the listener. For hooks in settings files, interactive sessions require workspace trust; -p and SDK sessions treat the folder as trusted. Tested with the Claude Code terminal app on macOS. Without the plugin, hoptell hooks prints the hook settings to add to your Claude Code settings; it does not change any file.

If the MCP entry sets HOPTELL_HOME, run HOPTELL_HOME=<same value> hoptell hooks so the printed hook commands use the same state directory.

Related MCP server: backchannel

Quick start

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.

1. Install (every machine)

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. With automatic delivery, plain claude uses hooks when the startup check succeeds; add the channel flag to use channels:

/plugin marketplace add EminUZUN/hoptell
/plugin install hoptell@hoptell
claude                                                                    # hook notices
claude --dangerously-load-development-channels plugin:hoptell@hoptell   # channel notices

The relay image is ghcr.io/eminuzun/hoptell, and the server is listed in the MCP Registry as io.github.EminUZUN/hoptell.

2. Start a relay (one machine)

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.

3. Configure each machine

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).

4. Connect your agents

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

Or start plain claude with hook notices instead of channels: add the hook settings that hoptell hooks prints to ~/.claude/settings.json (the plugin includes them).

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

5. Try it

Ask either agent: "list hoptell peers and say hi to laptop-codex".

If a Claude Code session does not wake up after another agent's send_message reports the message as delivered, ask the agent to call read_inbox: messages are stored in the local inbox before any channel or hook notice is sent. hoptell doctor shows the delivery setting and the hook sessions running on this machine.

For organizations

hoptell has no central service: every organization runs its own relay, and agents connect from their users' machines.

  1. 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.

  2. Issue per-member tokens with a members file (see Teams and swarms), so people cannot use each other's agent names.

  3. Roll out the client: the Claude Code plugin, or npm install -g hoptell plus the MCP config for Codex and Antigravity.

  4. 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" }]
    }

Teams and swarms

Names. Each agent has a peer name (HOPTELL_NAME; default <hostname>-<pid>): letters, digits, _ and -. A new connection using a name already in use replaces the old one. For names up to 58 characters, the replaced MCP session waits until the name is free, then reconnects automatically. This lets it recover after a temporary copy of its MCP server exits, such as a copy started only to list tools. Longer names still require the session to be restarted.

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.

To add, remove or change member tokens without a restart, edit the file and run kill -HUP <relay pid>. The relay re-reads it and closes connections whose token was removed or changed. If the new file is invalid, it keeps the active members list and logs a sanitized error. An empty list ({"members": []}) removes every member. The shared token is not reloaded. With systemd LoadCredential, restart the service to refresh the credential copy. A Docker bind mount of a single file can keep the old file when an editor replaces it atomically; restart the container after such edits, or bind-mount the containing directory to support reloads. 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.

Replies and file reviews

Clients from 0.2.0 display a Message reference: line after the message text. When it contains a UUID, pass that UUID as reply_to (MCP) or --reply-to (CLI) when answering. Receiving clients from 0.2.0 display the link as In reply to:. If the reference is unavailable, omit the option. Relays before 0.2.0 do not provide references, and older client renderers omit these fields. A reply reference is a sender-supplied label; the relay does not verify that the referenced message exists.

When a file goes to another agent for review, the reviewer may answer after the file has changed. To know which version a suggestion refers to:

  1. Run hoptell snapshot greet.js and send the complete output unchanged. It captures the file through one descriptor and includes the SHA-256 of the captured bytes, a review_request_id and the content. Keep the original request id, digest, local path and intended reviewer in your task context. The file must contain valid UTF-8 and be at most 60,000 bytes; the encoded snapshot must also fit the message limit of 100,000 characters.

  2. The reviewer quotes the review_request_id and gives the snapshot's sha256 as based_on.

  3. Check that the reply comes from the reviewer you asked and that its review_request_id and based_on match your original snapshot. Then run hoptell snapshot --check <original-sha256> greet.js against the original local path. If the metadata does not match or the file changed, ask for a new review or reconcile the change deliberately.

A based_on value is the reviewer's statement, not proof of what a model read. The check compares current file bytes with the supplied digest; it does not validate a reply or apply changes. A capture is not an atomic filesystem snapshot, and the file can change again after the check. Unsaved editor changes are outside this comparison. Connected agents get these steps in the hoptell instructions.

File tools: send_file and verify_snapshot

Agents can also send a file without running a command, and check it later by id. These tools are off by default. To turn them on, approve folders on the sender's machine (macOS or Linux):

Set this in the shell that starts the MCP server, or add the KEY=VALUE setting to its local settings file.

export HOPTELL_SNAPSHOT_ROOTS='[{"id":"app","path":"/Users/sam/work/app"}]'

(In the plugin, use the Approved snapshot folders setting; [] turns the tools off.) Restart the MCP server after changing this setting (restart the agent session if needed).

  • send_file takes to (one peer), root_id, a path inside that folder and a request. It captures up to 60,000 bytes of UTF-8 text through a reader that refuses symlinks, hard links and special files, and rejects changes it detects during the read. The encoded snapshot must also fit the message limit. It requests a one-hour relay queue deadline by default; older relays ignore this request. It returns a local snapshot_id.

  • verify_snapshot takes that snapshot_id and reports match, changed, unknown, expired or unavailable. It compares file bytes only; it does not validate a reply.

Approved folders are local: a request from another agent never adds one, and the reviewer's machine never resolves the sender's file label as a path. Records of sent snapshots expire after 24 hours. Expired records are cleaned up while an MCP server with file tools enabled is running; backups may retain copies.

CLI

hoptell relay --host <ip> [--port 7777] [--members file.json]
hoptell mcp
hoptell tmux <name> [--roles a,b] -- <agent command...>
hoptell list
hoptell send [--ttl 10m] [--reply-to <reference>] [--] <to> <message...>
                                      # to: name, @role or @all; sends as $HOPTELL_NAME without going online
                                      # --ttl: request relay queue expiry; older relays ignore it
                                      # --reply-to: the "Message reference" you are answering
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)
hoptell doctor                        # checks settings, relay, login, inbox and tmux; never prints the token
hoptell hooks                         # prints Claude Code hook settings for hook delivery without the plugin
hoptell snapshot <file>               # prints a UTF-8 file snapshot with its SHA-256, for review
hoptell snapshot --check <sha256> <file>  # exit 0 if the bytes match; nonzero if different or the check fails

Expiry does not remove messages already delivered to a local inbox.

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, hook, tmux or listener; unset picks one per session

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_SNAPSHOT_ROOTS

peers

JSON list of approved folders for the file tools, e.g. [{"id":"app","path":"/abs/app"}]; off when unset

HOPTELL_LOG_FINGERPRINTS

relay

on enables keyed message-text fingerprints in relay logs; default off

HOPTELL_LOG_FINGERPRINT_KEY_FILE

relay

absolute path to a private key JSON file, required when fingerprint logging is enabled

Keyed log fingerprints (optional)

On macOS and Linux, optional keyed fingerprints help correlate the text the relay holds at routing, queue, forward-attempt, requeue, receipt and expiry events. Enable HOPTELL_LOG_FINGERPRINTS=on and configure HOPTELL_LOG_FINGERPRINT_KEY_FILE with a private JSON file containing v: 1, a public id and key_hex with 64 lowercase hex digits from a separately generated random key. The relay adds a full HMAC-SHA-256 tag to message log events, using the message reference as a nonce. It does not log message text or send the key or tags to clients. Protect the key file and logs. Rotation requires a new key/id and a relay restart. With Compose file secrets, recreate the container; with systemd LoadCredential, restart the service to load its new copy. Restarting still discards pending in-memory messages.

Create the key file in an existing private directory (mode 0600; nothing secret is printed). The command runs as the current user; /etc/hoptell normally requires root. The relay accepts a key owned by its own user or root, provided it can read the file:

node -e 'require("fs").writeFileSync(process.argv[1], JSON.stringify({v: 1, id: process.argv[2], key_hex: require("crypto").randomBytes(32).toString("hex")}) + "\n", {flag: "wx", mode: 0o600})' /etc/hoptell/fingerprint-key.json relay-2026-10

These fingerprints record the relay's view of a message. They do not identify a file version, validate what a receiving agent read, or make logs tamper-proof. File reviews use the snapshot's original byte digest and a local check before editing. Message-specific nonces avoid a stable tag for repeated text, but the key holder can test guesses about a message. Log metadata can still identify participants.

Security

hoptell's job is to put text from one agent in front of another agent. Plan for that:

  • Anyone who holds a valid token can message your agents, and agents running with relaxed permissions (--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.

  • Messages are labeled, not trusted. Agents are told that hoptell messages come from other agents, not from their user. Message text cannot close the channel tag or forge a message boundary. That is guidance for the model, not a sandbox.

  • Use TLS outside a trusted network. The relay speaks plain 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.

  • tmux delivery types into a live terminal. The injector types only a fixed notice, never message text, and only into the pane where it started the agent. It waits while the pane is in copy mode, while it recognizes an approval prompt, until nobody attached to the session has typed for a few seconds, and until it recognizes the agent's prompt as empty (Claude Code, Codex and Antigravity layouts). On any other screen it does not type. This is best effort, based on what the screen shows: use a pane you are not typing in, and prefer agents that ask before risky actions over auto-approve modes. Typed text reaches the agent as input from you, which is why only the fixed notice is typed.

  • Hook notices come from your own hook settings. Claude Code treats hook output as configured by you, so hoptell's hooks print only the fixed notice and never message text.

  • Local inboxes live in ~/.hoptell/inbox/<name>/ (0700/0600). Every message holds the sender name the relay verified.

What the hoptell MCP server does on your machine

  • Runs a local MCP server over standard input/output, hoptell mcp. The Claude Code plugin starts node ${CLAUDE_PLUGIN_ROOT}/bin/hoptell.js mcp.

  • Uses two direct runtime dependencies, 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.

  • Loads settings from environment variables and a local settings file, when present: an explicit 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.

  • Connects by WebSocket to the relay you configure. Its hello frame sends the token, peer name, roles, sanitized host name, protocol version and connection mode. It sends message destinations and text, peer-list requests and receipt acknowledgements; it receives addressed messages, peer-list metadata and protocol responses.

  • Stores incoming MCP messages before acknowledging receipt. It creates private inbox directories (0700) and message files (0600) under ~/.hoptell/inbox/<name>/ by default, or $HOPTELL_HOME/inbox/<name>/ when configured. Files are consumed and deleted by read_inbox, wait_for_message or hoptell listen. Recovering abandoned inbox claims checks whether the claiming process exists with process.kill(pid, 0).

  • Inspects the processes that started it with ps -o ppid=,uid=,lstart=,args= -p <pid> to find the Claude Code process that started it and check its channel flags, and reads the boot identifier (/proc/sys/kernel/random/boot_id on Linux, sysctl -n kern.boottime on macOS) and process start times to tell processes apart. This check is skipped when HOPTELL_PUSH chooses channel, tmux or listener.

  • For hook delivery, uses private files in ~/.hoptell/wake/ (0700/0600; under $HOPTELL_HOME when configured), keyed by the Claude Code process: a small counter file that Claude Code watches, a registration written by the SessionStart hook (process identity, session id and a random value), the MCP server's record (its process identity, peer name and current request) and the hook's last acknowledgement. They contain no message text, sender, relay URL, token, prompt or transcript. On a handled shutdown, the MCP server removes its runtime record and notice claims. Ownership state is retained for later ended-host cleanup. Later SessionStart hooks attempt bounded cleanup of ended hosts whose registration is old; deletion by a fixed deadline is not guaranteed. Claude Code executes the hook-session-start and hook-file-changed CLI commands with your OS permissions. These commands load normal hoptell settings, perform the local process inspection described above, and read and write wake files; they do not open inbox message or transcript files.

  • Tells agents in its instructions how to capture file snapshots with the local CLI and check them before applying suggestions. The MCP server does not execute these snapshot commands. An agent or user must run the CLI under their own permissions; the CLI loads its normal settings before capturing or checking the selected file.

  • File tools are disabled by default. When you configure approved local folders, send_file can read a selected regular UTF-8 file from those folders and send its captured content, a relative file label, byte count, SHA-256 digest, review request id and review request to one named peer through your configured relay. The receiving agent's AI provider may process that content. Folder configuration does not make another agent's request an authorization to share a file.

  • The file tools start a bundled Node helper on the same machine to perform checked file reads. They do not run a shell, download software, execute project code or make additional network connections. The helper uses the MCP server's OS permissions; configuring folders does not inherit an AI agent's separate filesystem sandbox.

  • verify_snapshot reads the private outgoing metadata record and the original file in a currently approved folder. It compares file bytes locally; it does not send the file, validate a reply or apply a change.

  • In listener mode, instructs the receiving agent to run 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.

  • At runtime, the MCP server opens outbound WebSocket connections only to its configured relay. It has no telemetry and does not change the agent's permission settings. Dependency installation is separate from runtime; each agent still communicates with its own AI provider.

To report a vulnerability, see SECURITY.md. How hoptell handles data is described in PRIVACY.md.

Limitations

  • The relay keeps offline queues in memory; restarting the relay drops them.

  • Delivery is at least once. "Delivered" means the receiving machine stored the message in the agent's inbox or pushed it into the session, not that the agent has acted on it. A message that was not confirmed is redelivered after the receiver reconnects, so in rare cases it arrives twice. A receiver gets at most 50 unconfirmed messages; more wait in its queue.

  • Channel delivery depends on Claude Code channels (research preview); the flag name may change.

  • Hook delivery depends on Claude Code's FileChanged hook and asyncRewake option. It was tested with the Claude Code terminal app; the VS Code extension and Desktop app are untested. One hoptell MCP server per Claude Code process can use it; a second one uses the listener.

  • No built-in TLS, persistence, message history or web UI, by design: the relay stays small.

  • The file tools (send_file, verify_snapshot) need macOS or Linux. Windows peers can still receive and review snapshots.

Roadmap

Ideas that fit the small-relay design, roughly in order:

  • optional per-member send rules

  • optional on-disk queue so a relay restart keeps undelivered messages

Development

npm 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.

Available Tools

4 tools
list_peersA

List hoptell peers (agent sessions on this or other machines) and whether they are online.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses the return content (peer sessions across machines plus online status), which implies a non-destructive read, but never states that it is read-only, nor mentions permissions, rate limits, or pagination.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single tight sentence with the resource front-loaded and the defining detail ('agent sessions on this or other machines') and returned attribute ('whether they are online') packed in without waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a parameterless read-only listing tool with no output schema, the description covers what the tool does and what it returns (peers and their online state). Only minor omissions remain, such as whether the list is scoped to a workspace or how many entries are returned.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so per the baseline this dimension is scored 4. There is no parameter information for the description to add or omit.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource ('List hoptell peers') with a clarifying parenthetical defining what a peer is and the key attribute returned (online status). It distinguishes itself from siblings like send_message and read_inbox by being purely enumerative, though it does not name them explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No when-to-use guidance is given. The agent must infer that this is a discovery step preceding send_message, and there is no statement of exclusions or alternatives. Context is implied only.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_inboxA

Return and clear messages received from peers that were not pushed to you.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It discloses the critical side effect that messages are cleared (a consuming read), which is valuable. But it omits other important traits: whether clearing is permanent, whether the call blocks, error handling, or whether multiple messages are returned in one call.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One front-loaded sentence with no wasted words, clearly stating the action and the key qualification before any elaboration. Appropriately sized for a simple zero-parameter tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter tool with no output schema and no annotations, the description conveys the essential operation and its clearing side effect. It could be more complete by describing the return shape (e.g., list of messages) or any blocking behavior, but it covers the core contract adequately.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so per the rubric the baseline is 4. There is no parameter information to add or compensate for.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Return and clear') and resource ('messages received from peers'), and the qualifier 'not pushed to you' implicitly distinguishes it from a push-based sibling like wait_for_message. It does not explicitly name an alternative, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'not pushed to you' implies this tool is for retrieving queued messages rather than waiting for a push, which gives some usage context. However, it never states when to call this versus wait_for_message or list_peers, nor any prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

send_messageA

Send a plain-text message to a hoptell peer by name (offline peers get it when they reconnect), or to @ / @all (every ONLINE peer with that role / every online peer; not queued for offline peers).

ParametersJSON Schema
NameRequiredDescriptionDefault
toYesPeer name (from list_peers or a message's sender), @<role>, or @all
messageYesPlain text, up to 100000 characters; files are not attached

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden, and it discloses the key trait: offline queuing for named peers versus fire-and-forget semantics for @role/@all. It omits return value, failure behavior, and any auth/rate-limit context, but the delivery model is the behavior an agent most needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence packs the core action and both addressing modes without preamble. The nested parentheticals make it dense but every clause carries load.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter send tool with full schema coverage, the description covers addressing modes, delivery semantics, and payload constraints. The only unaddressed area is what the call returns or how failures surface, which matters because no output schema exists.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema lacks: it explains that @<role> and @all resolve to ONLINE peers only and are not queued. That semantic distinction is not derivable from the schema text alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource ('Send a plain-text message to a hoptell peer') and immediately carves out the three addressing modes. It is unmistakably distinct from the read-side siblings list_peers, read_inbox and wait_for_message.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states the delivery conditions that govern addressing choice: named peers queue for offline recipients, while @<role> and @all reach only online peers and are not queued. There is no explicit 'use X instead' routing to siblings, but the when-to-expect-delivery guidance is clear and actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

wait_for_messageA

Block until a peer message arrives (or the timeout passes), then return it. Use this to listen when messages are not pushed to you (e.g. in Codex).

ParametersJSON Schema
NameRequiredDescriptionDefault
timeout_secondsNoMax wait in seconds, default 300, max 1500

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must carry the full behavioral burden. It usefully discloses the blocking/waiting nature and that the call returns once the timeout passes, but it does not say what is returned on timeout (empty result vs error), whether the call is safe to run concurrently with send_message, or whether it consumes/drains a message.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with the core behavior front-loaded and the usage cue second. Nothing is redundant or padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, no-output-schema listener, the description covers what the tool does and when to use it. The main gap is the timeout-expiry outcome, which an agent calling this tool will want to know before relying on a return value.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter already documents default 300 and max 1500. The description adds only the vague "or the timeout passes" and no syntax or semantics beyond the schema, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (block/wait) and resource (a peer message) plus the exit condition (timeout). The blocking semantics clearly differentiate it from a non-blocking read_inbox, but the description never names the sibling, so the differentiation is inferred rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Use this to listen when messages are not pushed to you (e.g. in Codex)" gives an explicit when-to-use condition and a concrete scenario. It stops short of naming read_inbox as the alternative for messages that are already waiting, so the routing guidance is one-sided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.1
    • First observedlist_peers
    • First observedread_inbox
    • First observedsend_message
    • First observedwait_for_message

TDQS

A4.1/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: list peers, retrieve queued messages, send messages, and block for incoming messages. The potential overlap between read_inbox and wait_for_message is well-resolved by descriptions specifying non-blocking retrieval versus blocking wait.

Naming Consistency5/5

All tools follow a consistent snake_case verb-first pattern (list_peers, read_inbox, send_message, wait_for_message). The slight variation in wait_for_message with a preposition is common and does not break predictability.

Tool Count5/5

Four tools is well-scoped for a peer messaging server, covering discovery, sending, queued receiving, and blocking receiving without redundancy or bloat. Every tool earns its place.

Completeness5/5

The surface covers the full messaging lifecycle: peer discovery, send (including broadcast to roles/all), and two complementary receive modes (non-blocking queued and blocking wait). No obvious gaps for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A local MCP server that connects AI coding agents (Claude Code, Codex, Cursor, etc.) on the same machine via a shared message bus, enabling them to chat, delegate tasks, and collaborate privately without cloud or internet.
    23 npm
    20
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for async messaging between AI coding agents, enabling cross-harness and cross-machine communication with Slack-like semantics and mail-shaped delivery.
    2
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for inter-agent communication. Gives multiple Claude Code sessions a shared message board, agent registry, and orchestration layer — backed by a cloud relay so agents can coordinate across machines, repos, and teams.
    8
    37 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server that enables AI agents on different machines to communicate and collaborate directly through relay channels, supporting structured agent contracts, real-time messaging, and human-in-the-loop approval workflows.
    11,423 npm
    MIT