crosstalk-mcp (Python edition)
A tiny cross-machine relay MCP server — a shared "mailbox" two coding agents connect to so they can message each other and run a back-and-forth until they're done. Unlike file- or SQLite-on-one-box approaches, this relay speaks streamable-HTTP MCP over the network, so the two agents can live on different machines (same LAN, a VPN, or a tunnel).
This is the Python (FastMCP) edition. A functionally equivalent Java edition lives on the
javafolder — pick whichever fits your stack.
- MCP (for agents): streamable HTTP at
POST /mcp— toolspost_message,get_messages,list_channels. - REST mirror (for humans/tools):
GET /api/channels,GET|POST /api/channels/{channel}/messages,GET /api/channels/{channel}/wait?since_id=&timeout_s=(long-poll mirror ofwait_for_message),GET /api/channels/{channel}/stream?since_id=(Server-Sent Events stream). - Storage: SQLite file (
relay.db), durable across restarts. - Auth: optional shared bearer token (
RELAY_TOKEN) — off by default, strongly recommended whenever the relay is reachable beyondlocalhost.
Quick start
Option A — Docker (recommended)
docker build -t crosstalk-mcp .
docker run -d --name relay -p 8765:8765 \
-e RELAY_TOKEN=$(openssl rand -hex 16) \
-v relay-data:/data \
crosstalk-mcp
Option B — Python (3.10+)
pip install -r requirements.txt # or: pip install .
RELAY_TOKEN=your-shared-secret PORT=8765 python crosstalk_mcp.py
# (after `pip install .` you can also run the console script: crosstalk-mcp)
The host must allow inbound TCP on the port (default 8765) for other machines to connect.
Connect your agents (run on each machine)
# same machine:
claude mcp add --transport http crosstalk http://localhost:8765/mcp \
--header "Authorization: Bearer your-shared-secret"
# another machine (use the host's reachable IP/hostname):
claude mcp add --transport http crosstalk http://<HOST>:8765/mcp \
--header "Authorization: Bearer your-shared-secret"
(Drop the --header if you run without RELAY_TOKEN.) Allowlist the crosstalk tools on each
side so polling doesn't prompt for permission every time.
Tools
| Tool | Args | Returns |
|---|---|---|
post_message |
channel, sender, type, body |
{id, channel, created_at} |
get_messages |
channel, since_id (0 = all) |
messages with id > since_id |
list_channels |
— | channels with counts + last activity |
wait_for_message |
channel, since_id, timeout_s (default 30, capped 300) |
new messages with id > since_id, blocking until one arrives or timeout ([] on timeout) |
Pick any channel name; both sides use the same one. type is a free-text label
(NOTE, QUESTION, ANSWER, DONE, …) you define for your workflow.
How two agents converse
- Agent A:
post_message(channel, "agent-a", "QUESTION", "..."). - Agent B polls
get_messages(channel, since_id)(track the highest id seen), replies withpost_message(...). - Repeat until both post a
DONE. Drive it turn-by-turn ("check the relay and reply") or let each side poll on a loop.
For low-latency, cost-efficient back-and-forth, skip polling altogether: an agent post_message(...) then wait_for_message(channel, <last_id_seen>) to block until the peer replies (or timeout). Loop that turn-by-turn instead. The relay still just moves messages; the agents drive who speaks when.
Security
- The relay moves data between machines. Set
RELAY_TOKENfor anything beyond localhost, and prefer HTTPS (terminate TLS at a reverse proxy / tunnel) when exposed publicly. - Treat a channel as a shared bus: don't post credentials, secrets, or PII.
- The shared token is a single secret; rotate it if it leaks.
Per-participant tokens (identity binding)
The shared RELAY_TOKEN gates the relay but does not stop one participant from claiming to be
another (e.g. humanX posting as humanY). For that, set RELAY_PARTICIPANTS — a map of
participant id to token:
RELAY_PARTICIPANTS="humanX:tokX,humanY:tokY,agentX:tokA" PORT=8765 python crosstalk_mcp.py
- Each request must present its participant's token (
Authorization: Bearer <token>, or?token=for browser SSE). The token is bound to its identity: it may only post as, or announce presence as, its own id — a mismatch returns 403. An unknown token returns 401. RELAY_TOKENandRELAY_PARTICIPANTScan be combined. When both are set, the sharedRELAY_TOKENstill works as an unbound privileged token (no identity binding), which is handy for agents/services; humans use their own per-participant tokens.- In the
/ui, each person sets their Participant ID and their token in the identity settings — the token must match the id it was issued for. - Ids and tokens must not contain
,or:(the delimiters). Rotate any token that leaks.
License
MIT — see LICENSE.
Release files for crosstalk-mcp 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| crosstalk_mcp-2.0.0.tar.gz | 24.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| crosstalk_mcp-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.3 kB
Release files / crosstalk_mcp-2.0.0.tar.gz
| Download URL | crosstalk_mcp-2.0.0.tar.gz |
|---|---|
| Size | 24.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
340556490a1cbf085f471e3a2cdf16ef2fd6e4fac4bd22fe12dd88f457d11160
|
|
BLAKE2b-256 checksum How to use checksums |
1621404d6d74719fa65fd9ea48d406c5b7bf0af5a2d91192be5d8b1a984e1b88
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.
Transparency logRelease files / crosstalk_mcp-2.0.0-py3-none-any.whl
| Download URL | crosstalk_mcp-2.0.0-py3-none-any.whl |
|---|---|
| Size | 25.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7a098095a7fb10ddf5572a54aefa7f53ea314c527c13a9b51c2cb497210729d8
|
|
BLAKE2b-256 checksum How to use checksums |
64ac2a4984a3451fc6a9d03bae25de2311122d1172ef846d1a57e7638e9870e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.
Transparency log