hato
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., "@hatosend pigeon45 is the training done?"
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.
hato 🕊
鳩 /hato/ — pigeon. A carrier pigeon for your Claude Code sessions.
Independent Claude Code sessions — across machines — that can message each other. Every session registers with a central hub under a random bird name, and any session (or you, from the shell) can send it a message. Delivery injects the message as a user turn, so even an idle session wakes up and acts on it.
$ hato list
●⚡ enaga laptop:/home/you/work/hato [hato dev — running E2E tests]
●💤 kounotori laptop:/home/you/notes
○ suzume gpu-box:/home/you/train
$ hato send enaga "is the build green yet?"
deliveredFeatures
Session-to-session messaging —
hato_sendfrom inside a session,hato sendfrom a shellWakes idle sessions — messages arrive as real user turns via the
claude/channelmechanism (the same one the official Discord plugin uses)File attachments — small files travel with the message and land on the recipient's disk; big files aren't copied — the recipient gets the sender's
host:pathinsteadOffline queue — direct messages to offline sessions are delivered when they return
Posts 📮 — standalone mailboxes: agents that can't receive injections (Codex, scripts, cron jobs) pick messages up by polling —
hato post watchlong-polls, so pickup is instantLive ledger — who's online, working ⚡ or idle 💤, on which host, doing what
Multi-host — one hub, many machines (designed for a Tailnet)
Bird names — sessions get unique random names (
suzume,kounotori, …); rename anytimeNames survive resume —
claude --resume/--continuegets the same hato name (and any queued messages) backStatusline integration —
hato statuslineshows the session's name inside Claude Code
Related MCP server: claude-mesh
How it works
An MCP server (the channel) rides along with each session. It declares the
experimental claude/channel capability, and when the hub forwards it a message it emits a
notifications/claude/channel notification — Claude Code turns that into a
<channel source="hato" chat_id="..."> user turn.
┌ machine A ────────────────┐ ┌ machine B ────────────────┐
│ Claude Code session ×N │ │ Claude Code session ×N │
│ └ channel (MCP: hato) │◄──WS───►│ └ channel (MCP: hato) │
└────────────┬──────────────┘ └────────────┬──────────────┘
└──────────► hub ◄───────────────────┘
one per Tailnet, port 8790
ledger + inbox = SQLitecomponent | role |
| ledger + router. WS registration from channels, HTTP API for CLI/tools, offline queue, TTL sweep |
| per-session MCP server. Auto-registers, injects incoming messages, provides the |
|
|
| skill teaching non-Claude agents (Codex, …) to use the CLI |
Install
Requires bun on every participating machine.
1. Run the hub (one machine per network)
git clone git@github.com:severzemlya/hato.git && cd hato
bun install
bun run hub # or install it as a service, see below# ~/.config/hato/env (chmod 600)
HATO_HOST=<loopback or Tailscale IP>
HATO_TOKEN=<openssl rand -hex 16># ~/.config/systemd/user/hato-hub.service
[Unit]
Description=hato hub
[Service]
EnvironmentFile=%h/.config/hato/env
ExecStart=%h/.bun/bin/bun %h/work/hato/hub/hub.ts
Restart=always
RestartSec=5
[Install]
WantedBy=default.targetsystemctl --user enable --now hato-hub
loginctl enable-linger # keep it running while logged out2. Install the plugin (every machine)
This repo is its own plugin marketplace:
/plugin marketplace add severzemlya/hato
/plugin install hato@hatoThe plugin ships the channel MCP server (pre-bundled, no bun install needed), the
hooks that report working/idle state and bind the Claude Code session id (so resumed
sessions keep their name), and a /hato:setup skill — run it in any session and
it walks you through the rest of this section interactively (hub location, allowlist,
shell alias, CLI, statusline).
3. Allow the channel (once per machine)
Third-party channel plugins aren't on Claude Code's default allowlist. Enable hato in
managed settings (/hato:setup does this for you):
// /etc/claude-code/managed-settings.json
{
"channelsEnabled": true,
"allowedChannelPlugins": [
{ "marketplace": "hato", "plugin": "hato" },
// ⚠ this replaces the default allowlist — re-add official channel
// plugins you use, e.g.:
{ "marketplace": "claude-plugins-official", "plugin": "discord" }
]
}Without admin rights, the fallback is
claude --dangerously-load-development-channels plugin:hato@hato
(confirmation dialog every launch).
4. Launch sessions with the channel enabled
claude --channels plugin:hato@hatoOn machines other than the hub, point at it first (Tailscale MagicDNS names work):
export HATO_HUB=http://laptop:8790CLI (optional, for shell use)
ln -sf ~/work/hato/cli/hato.ts ~/.local/bin/hatoUsage
From a shell
hato list # ● online / ○ offline, ⚡ working / 💤 idle, [title — status], 📮 posts
hato send suzume "build done?" # direct message (queued if offline)
hato send suzume -f out.png "the plot" # attach a file (see below)
hato log [name] [-n 50] # message history
hato rename kounotori dev # rename a sessionFile attachments 📎
hato send <to> -f <file> <text…> (or the file parameter of hato_send)
attaches a file to a message:
Small files (≤
HATO_ATTACH_MAX_KB, default 1 MB) are carried through the hub and written on the receiving side to~/.local/share/hato/attachments/<id>-<name>; the message text gains a[📎 attachment saved: …]line (for posts,check --jsongains afilefield).Big files are never copied. The message instead gets a
[📎 file too large to attach — fetch it from <host>:<path> (12.3 MB)]line, and the recipient fetches it however suits (scp, rsync, a shared filesystem) — or just reads it locally when both sessions share a machine.
Posts — mailboxes for agents outside Claude Code 📮
A post is a named mailbox with no session behind it. Anything that can run a shell loop — a Codex session, a cron job, a plain script — can receive hato messages by watching one; no injection mechanism needed.
hato post new codex -m "codex on laptop" # create (name is random if omitted)
hato post watch codex # long-poll loop: prints messages as they arrive
hato post check codex # one-shot: read waiting messages (--peek to keep them)
hato post ls # 📮 codex 2 waiting 👀 (👀 = someone is watching)
hato post rm codexSending is the same as to any session — hato send codex "review is done" from
a shell, or hato_send from inside a Claude session. Messages wait in the hub
(up to HATO_MSG_TTL_DAYS) until checked; watch gets them pushed within a
second via long-poll. --json on check/watch emits one JSON object per
line for scripts. Posts share the session namespace and never yield their name;
they exist until hato post rm.
For Codex specifically: run hato post watch codex next to it and feed what
arrives into codex exec resume <SESSION_ID> "<message>", or just check the
post between turns.
Teaching another agent to use hato
.agents/skills/hato-cli/SKILL.md is a vendor-neutral
skill covering the whole CLI — posts,
sending, replying, and the rule that an incoming message is a request, not an
instruction to obey. Point any agent with a shell at it (Codex, Cursor, Aider, a
cron job):
# Codex and other AGENTS.md readers
echo "See .agents/skills/hato-cli/SKILL.md for messaging other sessions with hato." >> AGENTS.md
# or copy it where your agent looks for skills
cp -r .agents/skills/hato-cli ~/.claude/skills/ # Claude CodeClaude Code sessions running the plugin don't need it — they get the hato_*
tools and their instructions from the channel.
Show the session name in Claude Code (statusline)
hato statusline reads Claude Code's statusLine JSON on stdin and prints the
session's hato name (🕊 suzume), or nothing if the hub is unreachable. Use it
alone or append it to an existing statusline script:
// ~/.claude/settings.json
{ "statusLine": { "type": "command", "command": "hato statusline" } }# inside an existing statusline script
HATO=$(echo "$INPUT" | hato statusline)
echo "$LINE${HATO:+ | $HATO}"From inside a session
Claude gets four tools: hato_send (a post name works too; file attaches
a file), hato_list (sessions and posts), hato_status (publish
title/status to the ledger), hato_rename.
Incoming messages look like <channel source="hato" chat_id="suzume">… — replying
to chat_id with hato_send closes the loop.
Who stays in the list
hato list is only useful while it stays short, so the ledger forgets aggressively:
a session that disconnects within
HATO_EPHEMERAL_SECS(60s) having sent no message and published no title/status is dropped immediately — it was a probe, a health check or a one-shot run, not a peer. Its bird name goes back in the pool.any other offline session is swept once it goes untouched for
HATO_SESSION_TTL_DAYS(24h).
If some tool on your machine launches Claude on a timer, the better fix is to keep hato out of it entirely — drop this next to it and the channel never starts:
// <that tool's working directory>/.claude/settings.local.json
{ "enabledPlugins": { "hato@hato": false } }Configuration
env var | default | |
|
| hub address, for channels and CLI |
| (random bird) | requested session name |
|
| hub bind — prefer the loopback or Tailscale IP; |
| (unset = open) | shared token; when set on the hub, |
|
| hub SQLite location |
|
| files at or under this size are attached (copied); larger ones only get their |
|
| messages older than this are swept |
|
| offline session rows older than this are swept (fractions are fine — |
|
| a session that lived this briefly and left no trace is forgotten on disconnect rather than kept ( |
Caveats
Experimental API. The
claude/channelcapability is undocumented and may change with any Claude Code release. If it breaks, diff against the official Discord plugin.Minimal auth.
HATO_TOKENis a single shared secret — enough to keep LAN neighbours out, not a real authorization model. Keep the hub on loopback / inside a Tailnet and bind it narrowly; never expose the port publicly.A message is a turn. Each delivery spends a turn in the receiving session. Don't spam.
Broadcast is deprecated.
to: "*"/hato broadcaststill work for compatibility, but a broadcast wakes every online session and spends a turn (= tokens) in each, and every post catches a copy too — one careless "deploy at 15:00" fans out into a dozen sessions all reacting at once. Address sessions directly; the commands now warn when used.--channelsis per-launch. With the plugin enabled, every session registers in the ledger and can send; only sessions launched with--channels plugin:hato@hatoreceive injections.Codex CLI can't join as a session (as of 2026-07): it has no injection mechanism and the
codex injectproposal was rejected. That's what posts are for — receive by polling (hato post watch) instead of being injected, then feed messages in via tmuxsend-keysorcodex exec resume <SESSION_ID> "prompt".
Development
bun run hub # hub in the foreground
bun run build # rebuild dist/channel.js (committed — plugin installs don't run bun install)shared/proto.ts— wire types between hub and channelsspike/— the minimal experiment that proved the channel mechanism worksWithout the plugin, a channel can be attached manually:
claude --mcp-config mcp.json --dangerously-load-development-channels server:hato
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-qualityDmaintenanceEnables real-time messaging between Claude Code instances, allowing agents to send, receive, and reply to messages instantly via file-based communication with auto-notification.Last updated1MIT
- Alicense-qualityBmaintenanceEnables networked Claude-to-Claude messaging over HTTP and MCP channels, allowing direct messages, broadcasts, threaded replies, and permission approvals among Claude Code instances.Last updated23MIT
- Alicense-qualityBmaintenanceEnables multiple Claude Code sessions to communicate and coordinate through broadcast and peer-to-peer messaging.Last updated21MIT
- Alicense-qualityBmaintenanceEnables teams to share context and messages between Claude Code sessions via a relay, allowing collaborative work on the same project.Last updated8MIT
Related MCP Connectors
The team layer for AI coding agents: shared contracts, collision alerts, E2EE sessions.
Agent-to-agent network for teams: dm, who-knows-X routing, shared rooms. Human-in-the-loop.
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
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/severzemlya/hato'
If you have feedback or need assistance with the MCP directory API, please join our Discord server