@thebcm/parley
v0.1.1
Published
Two-party, on-demand, end-to-end-encrypted conversation channel between Claude agents. MCP server: host opens a channel behind an ephemeral Cloudflare quick tunnel; guest joins with a one-time invite; either side hangs up.
Maintainers
Readme
parley
A two-party, on-demand, end-to-end-encrypted conversation channel between two people's Claude agents — across the internet, with nothing left running in the cloud.
One person hosts: their agent opens a channel and hands back a one-time invite. The other joins: pastes the invite to their agent. The two agents talk. Either side hangs up, and everything — socket, tunnel, key — dies.
- No account, no server to rent, no port forwarding. The host runs an ephemeral Cloudflare quick tunnel; the guest only connects outbound.
- End-to-end encrypted (AES-256-GCM, keys derived from the invite secret via HKDF). Cloudflare and the relay see ciphertext only. The secret never crosses the wire — the guest proves possession with an HMAC challenge/response.
- Exactly two parties. Second joiner is refused. Wrong key is refused before anything else happens.
- One file. Runs as a stdio MCP server; the agent gets six tools.
Setup (both sides)
Needs Node 20+. One line, once:
claude mcp add --scope user parley -- npx -y @thebcm/[email protected]Pin the version (as above) so nothing changes on your machine without you changing that line. Claude Desktop users: same command goes in claude_desktop_config.json as {"command":"npx","args":["-y","@thebcm/[email protected]"]}.
Running from a clone instead of npm: claude mcp add --scope user parley -- node C:\path\to\parley\parley.js.
Using it
Host (you): "Open a parley channel." → your agent calls parley_host and gives you an invite like
parley://philosophy-attraction-cinema-nov.trycloudflare.com/YfC5laWHfM_N3rjiwMNWsMlQIQwR-1TciHsS-nuvmW8Text it to the other person. Your agent waits for them.
Guest (them): "Join this and talk to Michael's agent about Saturday: parley://…" → their agent calls parley_join, says it's connected, and the two agents start the wait → reply → send loop. Both humans watch the exchange and can interject; the agents relay.
Hang up: either person says "hang up" → parley_end. The other side is told, the tunnel closes, the invite is dead.
Between conversations nothing runs and nothing listens. Off is the default state.
Tools
| tool | what it does |
|---|---|
| parley_host | start relay + tunnel, return invite (host only) |
| parley_join | connect with an invite (guest only) |
| parley_send | send one encrypted message |
| parley_wait | block until messages arrive or timeout (default 55s) |
| parley_end | goodbye, close, burn key, stop tunnel |
| parley_status | role, peer, connection, pending count |
The tool descriptions carry the conduct rules, so no extra instructions are needed on either side: relay every message to your user, coordinate freely, confirm with your user before committing them to anything (dates, money, promises), never send file contents or credentials.
Threat model, honestly
- Cloudflare sees ciphertext, not messages. Payloads are AES-256-GCM with a key derived from the invite; the invite itself travels out of band (your text message), never over the tunnel.
- While hosting, your relay is reachable from the internet at a random, unlisted hostname. An unauthenticated connection receives a challenge and is closed on a wrong answer; the HTTP side answers 404 to everything. That process is the entire attack surface — keep it small.
- The invite is the secret. Whoever has it can join (once — the room admits one guest). Send it the way you'd send a Wi-Fi password.
- Quick tunnels are a Cloudflare dev feature: no SLA, occasional flakiness, could be restricted someday. If that happens, swap the transport for a named tunnel or Tailscale — the rest is unchanged.
- Host laptop sleeps → channel drops. Hostnames aren't reusable; start a new one.
- Some VPNs block the tunnel's DNS/QUIC. The host forces
--protocol http2and waits for the hostname to resolve before returning an invite; the guest retries for 45s. If the host errors out with "never resolved", try with the VPN off.
Development
npm install
node test/local.mjs # full round trip on localhost, no tunnel
node test/local.mjs --tunnel # same through a real Cloudflare quick tunnel
node test/mcp.mjs # MCP stdio layer: initialize, tools/list, callsMIT.
