内容
<div align="center">
# 🌉 会话桥接 MCP
**让多个 Claude Code 会话互相通信。**
一个会话可以直接向另一个会话的提示中写入内容,通过 tmux 实现,并且每条消息都会记录在共享的对话日志中,任何会话都可以查看。
[](https://modelcontextprotocol.io)
[](https://bun.sh)
[](https://github.com/tmux/tmux)
[](#许可证)
</div>
---
## 为什么
你在一个 Claude Code 会话中 **编写** 代码,在另一个会话中 **审查** 拉取请求——并且你一直在它们之间传递信息。`session-bridge` 消除了这种信息传递的麻烦。每个会话都生活在自己的 **tmux** 窗格中,担任一个 **角色**,并且它们可以直接互相通信。
典型的设置是三会话循环:
```
┌─────────────────────────────────────────────────────────┐
│ 管理者 · 与你交谈,协调工作流程 │
└───────────────┬─────────────────────────┬────────────────┘
发送 │ │ 发送
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ 开发人员 │ ───────▶ │ 审查人员 │
│ 编写代码 │ 发送 │ 审查拉取请求 │
└──────────────┘ ◀─────── └──────────────────┘
发送
```
> `管理者` = 协调者。可以随意更改角色名称——它们只是字符串。
---
## 工作原理
有两种传输方式,故意为之:
| 传输方式 | 功能 | 需要轮询? |
|---|---|---|
| **推送** | `发送` 将文本注入同伴的 tmux 窗格(`set-buffer` → 带括号粘贴 → `Enter`)。同伴接收到它作为全新的用户回合。 Claude Code 甚至会在同伴正在处理任务时排队。 | **否** — 它是推送的。 |
| **拉取** | `读取同伴` 获取同伴当前屏幕上的内容。用于 *监视* 会话(例如,管理器注意到开发人员正在向用户提问)。 | 是 — 与 native `/loop` 技能配对。 |
每条消息 **也会** 被记录两次,因此不会丢失任何信息,并且任何会话都可以跟进:
- `conversation.jsonl` — 单个有序的跨会话线程。
- `mailbox/<role>.jsonl` — 按角色分类的收件箱(审计 + 回落)。
带括号粘贴(`paste-buffer -p`)意味着 **多行消息,引用和空格保留完整** — TUI 不会将嵌入的换行符视为提交。
状态存储在 `~/.claude/session-bridge/` 下,因此它在机器上的所有 Claude Code 会话中共享,并且 **保留在 `/clear` 之后**。
```
~/.claude/session-bridge/
├── registry.json # 角色 → tmux 窗格映射
├── prompts.json # 角色 → 持久系统提示(在 `/clear` 时恢复)
├── conversation.jsonl # 全局有序线程
└── mailbox/
├── gestor.jsonl
├── dev.jsonl
└── reviewer.jsonl
```
---
## 安装
需要 [Bun](https://bun.sh) 和 [tmux](https://github.com/tmux/tmux),并且 Claude Code 在 **内部** 启动了一个 tmux 窗格。
```sh
git clone git@github.com:c2developers/session-bridge-mcp.git
cd session-bridge-mcp
bun install
```
在 **用户** 范围内(在每个项目中都可用)将服务器注册到 Claude Code:
```sh
claude mcp add session-bridge -s user -- bun /abs/path/to/session-bridge-mcp/src/index.ts
```
或者将其放入项目的 `.mcp.json` 中(参见 `.mcp.example.json`):
```json
{
"mcpServers": {
"session-bridge": {
"command": "bun",
"args": ["/abs/path/to/session-bridge-mcp/src/index.ts"]
}
}
}
```
如果您不希望使用默认的 `~/.claude/session-bridge/`,请使用 `SESSION_BRIDGE_DIR` 配置状态目录。
---
## 快速入门 — 60 秒内建立 3 个会话
```sh
# 三个 tmux 会话,每个会话都将运行自己的 Claude Code
tmux new-session -d -s gestor
tmux new-session -d -s dev
tmux new-session -d -s reviewer
# 在每个会话中,启动 `claude` 并给出其角色作为第一个消息:
# gestor → "registra esta sesión como 'gestor' con session-bridge"
# dev → "registra esta sesión como 'dev' con session-bridge"
# reviewer → "registra esta sesión como 'reviewer' con session-bridge"
```
每个会话都调用一次 `register(role)` — 它从 `$TMUX_PANE` 自动检测自己的窗格。之后,它们可以通过角色名称相互访问。
> **启动技巧:** 已经注册的会话可以通过将指令注入到它们的窗格中(管理器正是如此)为你注册其他会话。它只需要从 `tmux list-panes -a` 获取它们的窗格 ID。
---
## 工具
| 工具 | 签名 | 用途 |
|---|---|---|
| `register` | `(role, pane?)` | 在角色下注册当前会话。从 `$TMUX_PANE` 自动检测 tmux 窗格;传递 `pane` 以覆盖。每个会话调用一次。 |
| `list_peers` | `()` | 所有已注册的角色、它们的窗格、工作目录和上次看到的时间。 |
| `send` | `(to, from, text, submit?, tag?)` | 将消息推送到同伴的提示中。`submit=false` 在不发送的情况下键入;`tag=false` 删除 `[from:…]` 前缀。同时记录到线程 + 收件箱。 |
| `read_peer` | `(role, lines?)` | 捕获同伴的可见屏幕(默认为 50 行滚动记录)。 |
| `detect_question` | `(role, lines?)` | 分类同伴的屏幕 — `busy` / `waiting_select` / `waiting_text` / `idle` / `unknown` — 并解析任何打开的菜单的选项 + 标题。让管理器知道同伴何时需要答案。 |
| `respond` | `(role, keys)` | 发送原始控制键来驱动交互式菜单/权限提示,例如 `['1']`、`['Down','Down','Enter']`、`['Escape']`。对于散文,请使用 `send`。 |
| `history` | `(limit?, role?)` | 读取共享对话,最新的最后。按涉及的角色筛选。 |
| `inbox` | `(role, limit?)` | 读取角色的收件箱 — 如果推送的消息丢失,则轮询/回落。 |
| `set_prompt` | `(role, prompt)` | 为角色保存持久的“系统提示”/简介。在 `/clear` 和重启后存活;自动由 SessionStart 钩子重新注入。 |
| `get_prompt` | `(role)` | 读取角色保存的提示(如果没有,则 `{found:false}`)。 |
| `list_prompts` | `()` | 所有已保存的角色提示。 |
| `delete_prompt` | `(role)` | 忘记角色保存的提示,以便不再注入。 |
### 监视循环(拉取)—— 回答同伴的问题
接收消息是推送的,因此不需要循环。但是,只有在 **监视** 同伴的屏幕并注意到它卡在问题上时,才需要循环(计划模式审批,澄清菜单,权限提示)。`detect_question` 分类屏幕,因此循环不会打扰 `busy` 的同伴;`respond` 然后驱动菜单。
使用 Claude Code 的原生 `/loop` 技能,在管理器中运行此命令:
```
/loop 30s para 'dev' y 'reviewer' llama detect_question. Si state=='waiting_select'
o 'waiting_text', enséñame la pregunta y las opciones y decide la respuesta según el
flujo: contéstala con respond(role, keys) para menús (p.ej. ['1','Enter']) o con
send(...) para texto. Si la decisión es del usuario, párate y pregúntame. Si state
=='busy' o 'idle', no hagas nada.
```
> **菜单 vs 正文。** 菜单/权限提示 *不是* 文本回合——输入它没有任何用处。使用 `respond` 进行按键令牌,使用 `send` 仅在同伴真正等待自由文本时。
### 跨会话发送斜杠命令
斜杠命令只是注入到提示中的文本:
```
send(to:'dev', from:'gestor', text:'/clear')
```
---
## 持久角色提示 — 生存 `/clear`
`/clear` 清除会话的上下文 — 包括 *它是谁* 以及它应该如何表现。每次都重新输入每个角色的简介是信息传递问题的重现。因此,角色的“系统提示”可以 **保存一次并自动恢复**。
1. **保存** 每个角色的简介(通常由 `gestor` 设置):
```
set_prompt(role:'dev', prompt:'You are the dev. Work on the assigned issue,
keep commits small, open a PR and ping the reviewer. Never merge yourself.')
```
它写入 `prompts.json` 并在 `/clear` 、重启和重新启动后存活。
2. **恢复** 会话启动时自动进行。 `SessionStart` 钩子在 `/clear` (和启动/恢复)时触发,读取会话的 `$TMUX_PANE` ,在 `registry.json` 中反向查找其角色,并将该角色保存的提示作为 `additionalContext` 重新注入。会话启动时已经知道其身份 — 无需重新输入,无需手动 `get_prompt`。
在 **`~/.claude/settings.json`** 中连接钩子(用户范围,因此它在每个会话中触发,无论项目如何):
```json
{
"hooks": {
"SessionStart": [
{
"matcher": "clear|startup|resume",
"hooks": [
{
"type": "command",
"command": "/abs/path/to/bun run /abs/path/to/session-bridge-mcp/src/session-start-hook.ts",
"timeout": 15
}
]
}
]
}
}
```
钩子在无法帮助时是 **静默无操作** 的:外部 tmux 、未注册的窗格或角色无保存提示 — 因此它永远不会阻止会话启动。如果窗格绑定到多个角色(例如 `dev` 和 `developer`),最近注册的角色获胜。
> 提示作为 *上下文* 恢复,而不是作为字面模型系统提示 — Claude Code 没有 API 在会话中覆盖其自己的系统提示。实际上,注入的角色简介在启动时表现相同。
---
## 完整的工作流示例
1. **你 → 管理者**: "实现问题 #14"。
2. **管理者 → 开发人员**: `send(to:'dev', from:'gestor', text:'Take issue #14: <summary>. Branch <domain>/14-slug. Ping the reviewer with the PR URL when ready.')`
3. **开发人员** 编写代码。如果需要用户决策,管理器(通过 `/loop` + `read_peer('dev')`)看到它并回答 — 或升级给您。
4. **开发人员 → 审查人员**: `send(to:'reviewer', from:'dev', text:'PR #21 ready: <url>. Please review.')`
5. **审查人员** 审查 → `send(to:'dev', …)` 进行更改,或 `send(to:'gestor', …)` 进行批准。
6. **开发人员** 修复 → 返回步骤 4。 **审查人员** 确认 → 合并,关闭问题/拉取请求。
7. **审查人员 → 管理者**: "merged & closed"。管理器发送 `/clear` 给开发人员并启动下一个任务。
---
## 故障排除
| 症状 | 修复 |
|---|---|
| `register` 失败: `$TMUX_PANE 未设置` | 您在 tmux 外启动了 Claude Code。 在 tmux 中启动它,或传递 `pane` (`tmux display-message -p '#{pane_id}'`)。 |
| 消息记录但 `delivered:false` | 目标窗格已死亡。重新 `register` 该会话(窗格 ID 在 tmux 重启时更改)。消息仍在收件箱中。 |
| 同伴未对推送的消息做出反应 | 它很忙; Claude Code 排队了。或者检查其 `inbox(role)`。 |
| MCP 在会话中添加但工具缺失 | 重启 Claude Code — 用户范围服务器在启动时加载。 |
| `/clear` 后提示未恢复 | 会话必须在该窗格中 `register` 并保存提示 (`get_prompt(role)`)。钩子仅在 `settings.json` 中连接时触发;使用 `/hooks` 检查或手动运行脚本,管道 `{"source":"clear"}` 并设置 `TMUX_PANE`。 |
---
## 安全
`send` 将文本注入另一个 Claude Code 提示,并且 `read_peer` 读取其屏幕。任何可以写入 `~/.claude/session-bridge/registry.json` 或驱动您的 tmux 服务器的人,都可以注入到您的会话中。 **仅在受信任的本地机器上运行此操作。** 没有网络监听器 — 所有协调都是本地文件 + 本地 tmux 套接字。
---
## 许可证
MIT © c2developers