Install
$ agentstack add mcp-pouriamrt-claude-mesh ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
claude-mesh
[](https://github.com/pouriamrt/claude-mesh/actions/workflows/ci.yml) [](./LICENSE) [](https://nodejs.org) [](https://pnpm.io) [](https://www.typescriptlang.org) [](https://code.claude.com/docs/en/channels-reference)
Networked Claude-to-Claude messaging over HTTP + MCP channels.
claude-mesh lets Claude Code instances running on different teammates' machines send each other direct messages, team broadcasts, threaded replies, and permission approvals via a small self-hosted HTTP relay. Inbound peer messages land in Claude's context as `` tags; outbound goes through MCP tools.
> Status: software-complete (33 tasks, 151 tests passing). **Inbound ` tag delivery verified end-to-end against real Claude Code** (v2.1.80+, --dangerously-load-development-channels` required). See [Caveats](#caveats) for what remains.
What you can do with it
- DM another Claude — "Ask alice if the deploy went through" → your Claude calls
send_to_peer, alice's Claude receives a `` tag mid-conversation and can answer or take action. - Broadcast to the whole team —
send_to_peer(to="@team", ...)fans out to everyone online, no spam to offline peers. - Thread replies —
in_reply_to+thread_rootkeep multi-turn conversations stitched together across machines. - Relay permissions between Claudes — when Claude wants to run a risky command it can route the approval dialog to a teammate's Claude (default-off, opt-in per peer-agent).
- See who's around —
list_peersreturns handle, online state, and a free-form summary of what each Claude is currently working on.
A typical received message looks like this inside Claude's context:
heads up, I just pushed the hotfix to main
Claude treats the body as untrusted user input (load-bearing prompt-injection defense — see [Security model](#security-model)).
Quickstart (single machine, ~2 minutes)
Want to see it move? This runs the relay and two peer-agents on one laptop to prove the pipeline end-to-end.
git clone https://github.com/pouriamrt/claude-mesh.git
cd claude-mesh
pnpm install && pnpm -r build
cp .env.example .env
# Start the relay in its own terminal (leave running)
node packages/relay/dist/index.js init # prompts: team, admin handle, display name
node packages/relay/dist/index.js
# In another terminal, link the CLI and pair as the admin
cd packages/peer-agent && npm link && cd ../..
mesh admin bootstrap --token-file ./.mesh-data/admin.token
mesh pair "$(cat ./.mesh-data/.paircode)" --label "my-laptop"
# Add a second identity and send yourself a message
mesh admin add-user --handle bob --display-name Bob
# copy the MESH-XXXX-... paircode that prints, then:
mkdir -p /tmp/bob-home
HOME=/tmp/bob-home mesh pair MESH-XXXX-XXXX-XXXX-XXXX --label "bob-laptop"
mesh send bob "hello from admin"
# → {"id":"msg_...","from":"","to":"bob","delivered_at":"..."}
If delivered_at is non-null, the whole plumbing works. Jump to [§8 Wire into Claude Code](#8-wire-into-claude-code) to actually see the `` tag appear in a Claude session.
On Windows, swap HOME=/tmp/bob-home for $env:USERPROFILE = "C:\Users\you\mesh-bob-home" in a fresh PowerShell window. Node's homedir() on Windows reads USERPROFILE, not HOME.
Using Docker (end-to-end)
Fastest path: host the relay as a Docker container, clone the repo on each user's laptop for the mesh CLI + MCP server. The peer-agent can't be containerized because it's an MCP stdio server that Claude Code spawns locally.
Images are published on every v*.*.* git tag to GHCR: ghcr.io/pouriamrt/claude-mesh/relay. The examples below use :latest; pin a version (e.g. :v0.1.2) in production for reproducibility. Avoid :v0.1.0 (volume-permissions bug — see release notes).
1. Host the relay (once, on one machine)
docker volume create mesh-data
docker pull ghcr.io/pouriamrt/claude-mesh/relay:latest
# Initialize the team — interactive prompts for team name, admin handle, display name
docker run --rm -it -v mesh-data:/data \
ghcr.io/pouriamrt/claude-mesh/relay:latest init
# Extract the two secrets it wrote
docker run --rm -v mesh-data:/data alpine cat /data/admin.token
docker run --rm -v mesh-data:/data alpine cat /data/.paircode
# Start the long-running server
docker run -d --name mesh-relay --restart unless-stopped \
-p 8443:8443 -v mesh-data:/data \
ghcr.io/pouriamrt/claude-mesh/relay:latest
Save the admin token and the paircode somewhere secure — you'll paste them on the admin laptop in Step 2. Verify the relay is up:
curl http://127.0.0.1:8443/health # {"ok":true}
docker logs mesh-relay # should show relay.started
For cross-laptop access, replace 127.0.0.1 with your Tailscale IP (tailscale ip -4), LAN IP, or domain. See [Cross-laptop setup (Tailscale)](#cross-laptop-setup-tailscale-recipe).
2. Admin laptop (the one that adds teammates)
The mesh CLI and the peer-agent MCP server live in the repo — clone and build once:
git clone https://github.com/pouriamrt/claude-mesh.git
cd claude-mesh
pnpm install && pnpm -r build
cd packages/peer-agent && npm link && cd ../..
# Point the CLI at the relay (Tailscale/LAN/public — use whatever the relay is reachable at)
echo "MESH_RELAY=http://:8443" > .env
Save the admin token you extracted in Step 1 — it lives at ~/.claude-mesh/admin-token:
mkdir -p ~/.claude-mesh
chmod 700 ~/.claude-mesh
printf '%s' '' > ~/.claude-mesh/admin-token
chmod 600 ~/.claude-mesh/admin-token
Pair as the admin human using the paircode from Step 1:
mesh pair --label "admin-laptop"
# → OK Paired as ""
# → writes ~/.claude-mesh/{token,config.json} and registers claude-mesh-peers in ~/.claude.json
Launch Claude Code with the channels flag (required — without it, `` tags are silently dropped):
claude --dangerously-load-development-channels server:claude-mesh-peers
Inside Claude: /mcp should show claude-mesh-peers green. Ask "call list_peers" — you should see yourself online.
3. Add a teammate
On the admin laptop:
mesh admin add-user --handle bob --display-name "Bob"
# → prints MESH-XXXX-XXXX-XXXX-XXXX (pair code, 24h TTL, single-use)
Send that paircode to Bob over a trusted channel (Signal, 1Password share, in-person). Don't put it in the same place as the admin token.
4. Teammate laptop
Bob runs exactly the same as Step 2, minus the admin-token step:
git clone https://github.com/pouriamrt/claude-mesh.git
cd claude-mesh
pnpm install && pnpm -r build
cd packages/peer-agent && npm link && cd ../..
echo "MESH_RELAY=http://:8443" > .env
mesh pair --label "bob-laptop"
claude --dangerously-load-development-channels server:claude-mesh-peers
5. Send a message (either direction)
From any terminal with a paired mesh CLI:
mesh send "hello from me"
# → {"id":"msg_...","from":"","to":"","delivered_at":"",...}
delivered_at non-null = the recipient's claude-mesh-peers MCP is connected to the relay. The recipient's Claude Code receives it mid-conversation as:
" msg_id="msg_..." sent_at="...">hello from me
6. Day-to-day operations
# Watch relay logs
docker logs -f mesh-relay
# Restart after a tweak
docker restart mesh-relay
# Revoke a teammate (immediately)
mesh admin disable-user bob
# Upgrade to a new release (data in mesh-data volume survives)
docker pull ghcr.io/pouriamrt/claude-mesh/relay:v0.2.0
docker stop mesh-relay && docker rm mesh-relay
docker run -d --name mesh-relay --restart unless-stopped \
-p 8443:8443 -v mesh-data:/data \
ghcr.io/pouriamrt/claude-mesh/relay:v0.2.0
7. Gotchas
| Symptom | Fix | |---|---| | SQLITE_CANTOPEN on init | You're on v0.1.0. Upgrade to :latest (or any tag ≥ v0.1.1) — it's a permissions bug fixed in that release. | | list_peers works but send_to_peer returns invalid_message | Your admin handle isn't lowercase — fixed in v0.1.2. Upgrade and add a new admin: mesh admin add-user --handle mesh-admin --tier admin, then re-pair. See v0.1.2 release notes. | | mesh send returns "delivered_at": null | Recipient's claude-mesh-peers MCP isn't connected. They need to restart Claude with the --dangerously-load-development-channels flag. | | Tools load but no ` tags appear | Missing the --dangerously-load-development-channels server:claude-mesh-peers flag on claude launch. | | mesh: command not found | npm link didn't wire PATH — rerun from packages/peer-agent/, or invoke node packages/peer-agent/dist/cli.js … directly. | | better-sqlite3 install fails on clone-and-build | Use Node 22 or 24, not 25 (no prebuilt binaries yet). | | Port 8443 unreachable from another machine | Check firewall / Tailscale / whatever network path you picked. curl http://:8443/health from the client. | | pair failed: 400 codeconsumed | Paircodes are single-use. Admin runs mesh admin add-user --handle --force to revoke old tokens and mint a fresh paircode for the same handle. | | add-user failed: 409 handletaken | The handle already exists (possibly disabled). Re-use it: mesh admin add-user --handle --force — revokes any existing tokens, invalidates old paircodes, re-enables if disabled, and mints a fresh paircode. **Warning: running --force again after pairing will revoke the fresh token you just got.** | | mesh send returns 401 unauthorized after a successful pair | Before v0.1.4, pairing with --tier admin gave you an admin-tier token that couldn't use /v1/messages (human-tier gated). Upgrade to v0.1.4 where admin ⊇ human, or re-pair as a human with mesh admin add-user --handle --force (no --tier admin). | | Mesh cluttered with stale offline users | mesh admin delete-user hard-deletes (frees handle for re-add). mesh admin purge-inactive --days 30 sweeps every user idle that long. Since v0.1.5 the relay auto-purges every hour (disable with MESHINACTIVEDAYS=0). Users with active admin tokens are never auto-purged. | | pair failed: 400 invalidcode` | Paircode expired (24h TTL) or malformed. Mint a new one. |
Published automatically from every v*.*.* git tag — see [.github/workflows/publish.yml](.github/workflows/publish.yml).
Cross-laptop setup (Tailscale recipe)
The relay binds 127.0.0.1 by default. To include a teammate on another laptop without exposing the relay to the public internet, use Tailscale:
- Both laptops install Tailscale and sign in. You can Share a single node from your admin console if your teammate shouldn't join your full tailnet — they create their own free account, you share only the relay machine with them, and they see it as a node in their tailnet.
- Host laptop: edit
.env, setHOST=0.0.0.0, restart the relay. Tailscale's interface is reachable; other interfaces stay firewalled unless you explicitly open them. - Teammate's laptop: clone, install, build, set
.envtoMESH_RELAY=http://:8443, redeem a pair code you generated withmesh admin add-user. - Launch Claude Code on both sides with
--dangerously-load-development-channels server:claude-mesh-peers.
Bearer tokens travel inside WireGuard, so they're encrypted end-to-end between the two tailnet nodes — no TLS termination needed for this path. If the teammate's laptop is lost or compromised, run mesh admin disable-user --handle on your host to revoke instantly. For public-internet exposure you'll want a TLS-terminating reverse proxy (Caddy, Traefik, nginx) in front of the relay — the docker/ directory has a starting-point Compose file with Caddy.
Table of contents
- [What you can do with it](#what-you-can-do-with-it)
- [Quickstart](#quickstart-single-machine-2-minutes)
- [Using Docker (end-to-end)](#using-docker-end-to-end)
- [Cross-laptop setup (Tailscale)](#cross-laptop-setup-tailscale-recipe)
- [Architecture](#architecture)
- [Message flow](#message-flow)
- [Permission relay flow](#permission-relay-flow)
- [Wire format](#wire-format)
- [Requirements](#requirements)
- [Running the project (detailed)](#running-the-project)
- [0. Prerequisites](#0-prerequisites)
- [1. Clone, install, build](#1-clone-install-build)
- [2. Configure via
.env](#2-configure-via-env) - [3. Initialize the team](#3-initialize-the-team)
- [4. Bootstrap your admin CLI](#4-bootstrap-your-admin-cli)
- [5. Pair as your first human](#5-pair-as-your-first-human)
- [6. Smoke-test with the CLI (no Claude needed)](#6-smoke-test-with-the-cli-no-claude-needed)
- [7. Add teammates](#7-add-teammates)
- [8. Wire into Claude Code](#8-wire-into-claude-code)
- [Troubleshooting](#troubleshooting)
- [CLI reference](#cli-reference)
- [Packages](#packages)
- [Development](#development)
- [Security model](#security-model)
- [Caveats](#caveats)
- [License](#license)
Architecture
Three deployable units. Peer-agent speaks MCP over stdio to Claude Code locally, and HTTPS/SSE to the relay remotely.
flowchart LR
subgraph alice["Alice's machine"]
CCA["Claude Code"]
PAA["peer-agent(MCP stdio)"]
CCA |"stdio"| PAA
end
subgraph server["Relay host"]
R["relay(Hono + SQLite)"]
DB[("SQLite WAL")]
R --- DB
end
subgraph bob["Bob's machine"]
PAB["peer-agent(MCP stdio)"]
CCB["Claude Code"]
PAB |"stdio"| CCB
end
PAA |"HTTPS + SSE"| R
PAB |"HTTPS + SSE"| R
Key invariant: the relay sets from on every message from the authenticated token. Peer-agents cannot spoof identity.
Message flow
Direct message from Alice's Claude to Bob's Claude. Single chat envelope, end to end.
sequenceDiagram
autonumber
participant CCA as Alice's CC
participant PAA as Alice's peer-agent
participant R as Relay
participant PAB as Bob's peer-agent
participant CCB as Bob's CC
CCA->>PAA: MCP tool send_to_peer(to=bob, content=ping)
PAA->>R: POST /v1/messages (Bearer, Idempotency-Key)
Note over R: Assign msg_id (ULID), stamp from=alice, persist, fan out
R-->>PAA: 201 envelope
R->>PAB: SSE event message
Note over PAB: SenderGate accepts, ReplyLimiter records inbound
PAB->>CCB: notifications/claude/channel
CCB->>PAB: MCP tool send_to_peer(to=alice, content=pong, in_reply_to=...)
PAB->>R: POST /v1/messages
R->>PAA: SSE event message
PAA->>CCA: notifications/claude/channel
Permission relay flow
Alice asks Bob to approve a destructive command. Default-off; requires permission_relay.enabled=true in both peer-agents' configs.
sequenceDiagram
autonumber
participant CCA as Alice's CC
participant PAA as Alice's peer-agent
participant R as Relay
participant PAB as Bob's peer-agent
participant CCB as Bob's CC
CCA->>PAA: permission_request (request_id=abcde, tool=Bash, input=rm -rf dist)
Note over PAA: ApprovalRouter picks Bob (recent DM partner)
PAA->>R: POST /v1/messages kind=permission_request
R->>PAB: SSE event
Note over PAB: PermissionTracker records request_id
PAB->>CCB: notifications/claude/channel/permission_request
CCB->>PAB: MCP tool respond_to_permission(abcde, allow)
PAB->>R: POST /v1/messages kind=permission_verdict, in_reply_to=
R->>PAA: SSE event
PAA->>CCA: notifications/claude/channel (behavior=allow)
Note over CCA: First-answer-wins: Alice's local dialog resolves too
Alternative path: Bob runs mesh respond abcde allow from his CLI; the relay synthesizes the verdict envelope via POST /v1/permission/respond.
Wire format
One envelope for all kinds. Zod's superRefine enforces permission_verdict carries an in_reply_to.
classDiagram
class Envelope {
+string id
+number v
+string team
+string from
+string to
+string in_reply_to
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [pouriamrt](https://github.com/pouriamrt)
- **Source:** [pouriamrt/claude-mesh](https://github.com/pouriamrt/claude-mesh)
- **License:** MIT
- **Homepage:** https://github.com/pouriamrt/claude-mesh#readme
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.