peer-relay

peer-relay

A message relay MCP server enabling two separate Claude Code instances to exchange direct questions and answers asynchronously without sharing context.

Category
访问服务器

README

peer-relay

A deliberately dumb message relay that lets two separate Claude Code instances — run by two different developers, on two different machines — exchange direct questions and answers without sharing or merging their context.

It is an MCP server that behaves like a tiny mailbox. The only thing that ever crosses between the two instances is the literal text of a question and the literal text of an answer (plus a couple of small, self-written fields). Nothing else.

Relay, not knowledge base. peer-relay is intentionally dumb. No context syncing. No state mirroring. No background memory. No "let me read your repo for you."


Why

Two developers are working on related things. Dev A's Claude Code needs something only Dev B (or Dev B's codebase) knows — "why does the session token expire after 15 minutes?". Today you'd Slack the human, who asks their Claude, who answers, who pastes it back.

peer-relay lets Dev A's Claude ask Dev B's Claude directly and asynchronously, while keeping the two working contexts completely separate. A's repo, files, and conversation never touch B's, and vice-versa. Only the question and the answer move.


The trust model (what is and isn't stored)

This is the whole point, so it's worth being precise.

The relay stores ONLY these fields:

Field What it is
id An opaque message id.
room_token The shared pairing secret (so one store can host isolated pairs).
asker_id A short opaque label of who asked (e.g. alice) — for 1:1 routing.
question The literal question text.
context_hint Optional short free-text the asker writes themselves.
answer The literal answer text (once answered).
sources Optional array of short citation strings the answerer writes.
asked_at / answered_at ISO-8601 timestamps.

It never stores, inspects, or fuses:

  • file contents or file trees
  • repository state, diffs, or branches
  • conversation history or working memory
  • anything auto-collected from either developer's environment

Why you can believe that

The guarantee is structural, not a pinky-promise in prose:

  • The storage contract in src/storage/types.ts names every field that can exist. There is no generic metadata, payload, context, or blob field anywhere.
  • The SQLite schema in src/storage/sqlite.ts has exactly those columns and nothing else — no JSON catch-all column, no key/value side table. The only multi-value field, sources, is constrained to a flat array of strings.
  • context_hint and sources are written by the humans-in-the-loop Claudes themselves, in their own words. The server does not and cannot auto-collect them.

To make the relay store anything richer, you would have to change that interface and that schema — a visible, reviewable diff. That's the design.


Grounding answers (and not propagating nonsense)

The biggest risk in letting one Claude answer another is confident-but-unfounded claims crossing the wire and being treated as fact. peer-relay mitigates this at the prompt layer:

  • answer_question strongly prompts the answering Claude to ground its answer in concrete sources — file paths, file:line refs, commit hashes, PR numbers, decision docs.
  • It instructs the answerer to say so explicitly when it is unsure or answering from memory rather than a verifiable source.
  • check_answers reminds the asker to treat answers as claims from another developer, to prefer verifying cited sources, and to weight "from memory" answers accordingly.

Sources are encouraged but not required — sometimes the honest answer is "from memory, not verified."


MCP tools

Tool Signature Purpose
ask_peer (question: string, context_hint?: string) → { question_id } Enqueue a question for the peer. context_hint is short orienting text you write.
check_questions () → [{ question_id, question, context_hint, asked_at }] Peer polls for questions addressed to them.
answer_question (question_id: string, answer: string, sources?: string[]) → { ok } Post an answer back, ideally with grounding sources.
check_answers () → [{ question_id, question, answer, sources, answered_at }] Asker polls for answers to their questions.

The flow is poll-based and asynchronous: A ask_peer → B check_questions → B answer_question → A check_answers.


Quick start (Docker)

The whole thing is one container plus a shared secret. One of you hosts it; both of you connect.

1. Host the relay

git clone https://github.com/jmgomezl/peer-relay.git
cd peer-relay

# pick a shared secret — BOTH devs will use this exact value
echo "PEER_RELAY_ROOM_TOKEN=$(openssl rand -hex 32)" > .env

docker compose up -d

That's it. The relay is now on http://localhost:8787/mcp, with a persistent volume for the mailbox and a built-in health check. cat .env to get the token to share with the other dev.

No Docker? Use Node ≥ 20 instead: npm install && npm run build && PEER_RELAY_ROOM_TOKEN=... PEER_RELAY_HOST=0.0.0.0 npm start.

2. Make it reachable by the other developer

The other dev's Claude Code has to be able to hit that URL. Pick whichever matches your situation:

Situation What to use
Two laptops, different networks (most common) Expose it with a tunnel — zero server needed:<br>cloudflared tunnel --url http://localhost:8787  or  ngrok http 8787<br>Share the https://… URL it prints. This also gives you HTTPS (see the security note below).
Same office LAN / Tailscale / VPN Use the host's LAN/Tailscale IP: http://<host-ip>:8787/mcp.
A shared box / VPS / cloud Run the same docker compose up -d there, open port 8787 (ideally behind an HTTPS reverse proxy), use its address.

⚠️ Security: the server speaks plain HTTP and the room token is a bearer secret. Do not send it over the open internet unencrypted — put HTTPS in front (a tunnel does this for you, or use a reverse proxy like Caddy/Traefik). On a trusted LAN/VPN, plain HTTP is fine.

3. Each dev registers it in Claude Code

Both devs point at the same URL and same room token, but send a different x-peer-id. See examples/dev-a.mcp.json and examples/dev-b.mcp.json.

Dev A's .mcp.json (in their project root):

{
  "mcpServers": {
    "peer-relay": {
      "type": "http",
      "url": "https://your-relay-url/mcp",
      "headers": {
        "x-room-token": "the-shared-secret-from-step-1",
        "x-peer-id": "alice"
      }
    }
  }
}

Dev B's is identical except "x-peer-id": "bob". Restart Claude Code (or /mcp to reconnect) and the four peer-relay tools appear.

That's the entire pairing: same room token = same mailbox; distinct peer ids = the two sides.

4. Use it

In Dev A's Claude Code:

Ask my peer why the session token expires after 15 minutes — mention it's about the auth refactor in src/auth/session.ts.

Claude calls ask_peer. Later, in Dev B's Claude Code, "check questions from my peer" surfaces it; B answers with sources; A's "check for answers" retrieves it.

Heads-up — polling, no push. The relay does not notify either side. Each dev has to ask their Claude to "check for questions/answers." That's intentional for v1; a question waits in the mailbox until the peer polls.

Managing the container

docker compose logs -f      # watch it
docker compose down         # stop (mailbox volume is kept)
docker compose down -v      # stop and wipe the mailbox

The mailbox persists in the peer-relay-data volume across restarts. See .env.example for all env vars.


Pairing & auth model

  • One shared room token authenticates the room. Present it as x-room-token: <token> or Authorization: Bearer <token>. A wrong/missing token gets 401.
  • Peer id (x-peer-id) says which side you are. In a room, a question asked by alice is visible to everyone who is not alice — for a strict 1:1 pair, that's exactly bob.
  • That's all the auth there is in v1. No accounts, no OAuth. Keep your room token secret; rotate it by changing it on both sides.

What this is NOT

  • Not a context-sync tool. It does not mirror, merge, or replicate either developer's working context, files, or conversation. The two contexts stay fully separate.
  • Not a knowledge base. It doesn't index, embed, summarize, or "remember" anything for later retrieval. It's a queue of literal Q&A text, nothing more.
  • Not an auto-context collector. It never reads your repo or environment to enrich a question. If the peer needs orientation, the asker writes a context_hint by hand.
  • Not a team router. v1 is strictly one-to-one between two known devs. No fan-out, no channels-of-many.
  • Not an oracle. Answers are claims from another Claude. Grounding via sources is encouraged precisely because answers can be wrong.

Storage layer

Storage sits behind the Storage interface. The default implementation is SQLite (src/storage/sqlite.ts) for zero-setup local dev. To move to Postgres later, implement the same interface — no changes to the tools or transport. The narrow interface is also what keeps the trust model enforceable: there's simply no method to store anything beyond the documented fields.


Development

npm run dev        # watch-mode server (tsx)
npm test           # end-to-end test: A asks, B answers, A reads it back
npm run typecheck  # tsc --noEmit

The e2e test (test/e2e.test.ts) drives a full ask→answer→read cycle through the queue against an in-memory SQLite store, plus room isolation and double-answer rejection.


Roadmap (explicitly out of scope for v1)

  • On-chain / HCS audit trail as a separate signing layer (not in v1).
  • Team-wide routing beyond 1:1.
  • A web UI.
  • Auth beyond the shared room token.

License

MIT — see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选