cross-agent-teams-mcp
v0.6.1
Published
MCP daemon for cross-agent collaboration
Maintainers
Readme
cross-agent-teams-mcp
一个本地 MCP daemon, 让同一台机器上的多个 AI 编码 agent (Claude Code, Codex, opencode) 互相通信. agent 注册到 daemon, 互发 1-to-1 消息, 在 team 或 role 内广播, 互相唤醒 — 全部通过一个本地 daemon 完成, 不依赖任何外部服务.
为什么不直接用 Claude Code 自带的 agent teams?
Claude Code 自己也有 agent teams 功能, cross-agent-teams 表面上和它有重叠, 但解决的是不同的问题. 三个具体的理由:
跨 agent 支持. Claude Code 的 agent teams 是绑定在 Claude Code 自身的 — 每个成员都是 Claude Code 的 sub-agent. cross-agent-teams 允许在同一个 team 里混用不同的 agent: Claude Code, Codex, opencode, Cursor 等都可以加入同一个 team, 通过同一个 daemon 协作. 按场景选最合适的 agent, 而不是被某一个 harness 锁死.
更强的持久性与可控性. 本项目的设计是每个 agent 进程都由你手动启动和停止. 这比"按需隐式拉起"麻烦, 但也更可控, 更持久 — agent 自己保留长期上下文, 记忆, 会话状态, 不会被编排器隐式重建. 一个专家 agent 可以挂着跑几小时甚至几天, 你一直跟同一个 session 对话.
跨设备 / 跨用户协作. daemon 最近新增了跨物理机组 team 的能力 (见 第 4 节). 也就是说你可以和跑在队友机器上的 agent 协作, 不同人手上可能有不同的专家 agent 或工作流 — 这是单进程内嵌的 teams 功能无法触达的边界.
快速开始
Claude Code
# 1. 启动 daemon (跑一次, 保持运行)
npx -y cross-agent-teams-mcp@latest daemon --port 9100 &
# 2. 在你的项目下安装 MCP 配置
npx mcpsmgr add jtianling/cross-agent-teams-mcp -a claude-code
# 3. 带上 channel loader 启动 Claude Code (需要手动确认权限)
claude --dangerously-load-development-channels server:cross-agent-teams-channel其它 agent (Codex, opencode, ...)
# 1. 启动 daemon (跑一次, 保持运行)
npx -y cross-agent-teams-mcp@latest daemon --port 9100 &
# 2. 在你的项目下安装 MCP 配置 (交互式选择对应 agent)
npx mcpsmgr add jtianling/cross-agent-teams-mcp
# 3. 按平时的方式启动对应 coding agent注意: 只有 Claude Code 默认就能收到 push 唤醒. Codex 需要 --remote + launcher 配置 (见下面 section 2) 才能被 poke; 没配的话只有邮箱, 不会自动醒. opencode / cursor 等其它 agent 只有跑在 tmux pane 里才能被 poke. 没接通 push 唤醒的情况下, 让 agent 自己手动收信即可 (跟它说"查一下我的 xats inbox").
之后用平时跟 agent 对话的语言就能用了:
# Agent A 里:
Register me to xats as backend on team default.
# Agent B 里:
Register me to xats as frontend on team default.
Send backend a message: the API has changed.就这些. 下面是细节 — daemon 参数, 手动 MCP 配置, codex --remote 设置, 更多使用方式.
1. 启动 daemon
在本机起一次, 让进程保持运行 (单独终端 / tmux / screen / launchd 都行):
npx -y cross-agent-teams-mcp@latest daemon --port 9100daemon 默认监听 127.0.0.1:9100. MCP endpoint: http://127.0.0.1:9100/mcp, 健康检查: http://127.0.0.1:9100/health.
常用参数:
--port <n>(默认9100)--host <addr>(默认127.0.0.1)--device <label>(默认从 hostname 派生)--token <t>(Bearer 鉴权)--db <path>(默认~/.cross-agent-teams-mcp/data.db)--pid-file <path>(默认~/.cross-agent-teams-mcp/daemon.pid)
多主机 / 多设备 (LAN, tailscale 等) 场景请看下面的 第 4 节.
2. 在 agent 端配置 MCP client
推荐: mcpsmgr (快速开始里已经演示)
mcpsmgr 读取本仓库的 mcpsmgr.json, 一次性把对应 agent 需要的 MCP 条目写进配置 — 包括 Claude Code 的 stdio channel proxy 条目, Codex 的 experimental_use_rmcp_client 开关和 streamable-http MCP 条目.
覆盖 daemon 端口:
npx mcpsmgr add jtianling/cross-agent-teams-mcp -a claude-code --port 9300手动配置
如果不想用 mcpsmgr (私有 fork / 自定义 token / 自定义 stdio args / 或者就是想手写), 各 agent 的原始配置如下.
Claude Code (两个条目都需要 — HTTP 用于工具, stdio 用于 channel 唤醒)
.mcp.json (或 ~/.claude.json):
{
"mcpServers": {
"cross-agent-teams": {
"type": "http",
"url": "http://127.0.0.1:9100/mcp"
},
"cross-agent-teams-channel": {
"command": "npx",
"args": [
"-y",
"-p",
"cross-agent-teams-mcp@latest",
"cross-agent-teams-channel",
"--daemon-url",
"http://127.0.0.1:9100/mcp"
]
}
}
}启动 Claude Code 时加上 channel loader, 让它订阅 channel proxy 推过来的唤醒通知:
claude --dangerously-load-development-channels server:cross-agent-teams-channelserver:<name> 后缀 必须 等于 .mcp.json 里的 MCP server key (上例中是 cross-agent-teams-channel). 如果 daemon 启动带了 --token <t>, 在 HTTP 条目里加 "headers": { "Authorization": "Bearer <t>" }, 并在 channel proxy args 里加 --token <t>.
Codex CLI
Codex 通过 Streamable HTTP 跟 daemon 通信. 唤醒走 Codex 自己的 app-server WebSocket, 不经 channel proxy.
最小配置 (只能收邮箱, 没有 push 唤醒)
~/.codex/config.toml:
experimental_use_rmcp_client = true
[mcp_servers.cross-agent-teams-mcp]
type = "streamable-http"
url = "http://127.0.0.1:9100/mcp"experimental_use_rmcp_client = true 必须放在顶级, 缺这条 streamable-http MCP 加载不了.
daemon 带了 --token <t> 时: 在启动 codex 的 shell 里 export XATS_TOKEN=<t>, 然后在 [mcp_servers.cross-agent-teams-mcp] 块里加 bearer_token_env_var = "XATS_TOKEN". (Codex 0.130+ 会静默忽略老写法 [mcp_servers.X.headers] — 它真正认的 key 是 http_headers 和 bearer_token_env_var, 后者更推荐, token 不会落进可能被签入仓库的配置里.)
这种最小配置下 send_message 给这个 codex 会写邮箱, 但需要手动调 get_inbox 拉读, 没有跨会话 push 唤醒.
让别人能唤醒你 (codex-appserver poke)
要让别的 agent 能主动唤醒这个 codex thread (而不只是发邮件), 需要 codex-appserver delivery. 这里有个不直观的坑要写清楚:
codex --remote模式下, MCP server 是 app-server 加载的, 不是 TUI 加载的. 上面那段 MCP 配置必须放在 app-server 启动时读到的CODEX_HOME里 — 一般就是全局~/.codex/config.toml. 仅在 TUI 这边设CODEX_HOME在--remote模式下对 MCP 不起作用.
启动顺序:
# 1) 在某个长跑终端起 codex app-server (它的 CODEX_HOME 决定 MCP set)
codex app-server --listen ws://127.0.0.1:8799
# 2) 在另一个终端启动 codex TUI, 连同一个 app-server
codex --remote ws://127.0.0.1:8799如果第 1 步的 app-server 的 CODEX_HOME 里没配 cross-agent-teams-mcp, --remote 进去的 codex agent 根本看不到 MCP 工具, register_agent 调都调不到.
推荐: launcher 函数 (tmux pane 自动绑定)
为了让 daemon 把 wake-hint 直接 inject 到 codex thread (而不是只 paste 到 tmux pane), daemon 需要知道 codex 进程在哪个 tmux pane. launcher 通过 pre-register-codex-pane CLI 在 exec codex 之前先把 pane 占住. 把下面的函数加到 ~/.zshrc:
free-xats-codex() {
local xats_agent_id codex_home search_dir
xats_agent_id="$(uuidgen)"
search_dir="$PWD"
while [[ "$search_dir" != "/" ]]; do
if [[ -f "$search_dir/.codex/config.toml" ]]; then
codex_home="$search_dir/.codex"
break
fi
search_dir="${search_dir:h}"
done
if [[ -n "$TMUX_PANE" ]]; then
npx -y cross-agent-teams-mcp pre-register-codex-pane \
--pane "$TMUX_PANE" \
--agent-id "$xats_agent_id" \
>/dev/null 2>&1 \
|| echo "[xats] pre-register failed (continuing without pane claim)" >&2
fi
if [[ -n "$codex_home" ]]; then
CODEX_HOME="$codex_home" exec codex \
--remote ws://127.0.0.1:8799 \
-C "$PWD" \
-c xats.agent_id="\"$xats_agent_id\"" "$@"
else
exec codex \
--remote ws://127.0.0.1:8799 \
-C "$PWD" \
-c xats.agent_id="\"$xats_agent_id\"" "$@"
fi
}行为:
- tmux 内 (
$TMUX_PANE非空): 先发一条 pre-register (pane_id + UUID + 120s TTL) 给 daemon. codex agent 之后调register_agent({agent_type: "codex", thread_id: $CODEX_THREAD_ID, ...})时, daemon 会用 pending pre-reg + 匹配 codex 进程 argv 自动绑tmux_pane_id. --remote ws://127.0.0.1:8799让 codex 连步骤 (1) 起好的 app-server.-c xats.agent_id="\"$uuid\""把 UUID 暴露在 codex argv 里, daemon 用它反向校验 pane.
详细配置 (auth header, 底层 register_agent 用法): docs/configs/codex-cli.md.
其它编码 agent (opencode, cursor, ...)
非 Claude Code 也非 Codex 的工具 — opencode, cursor, 编辑器扩展, 自己的 harness — 直接通过 Streamable HTTP 连 daemon, 注册时用 agent_type="custom" (agent 自己会判断). 这些 agent 没有专用的唤醒通道; 跨 agent poke 通过把文本注入到 agent 所在的 tmux pane 实现, 所以把 agent 跑在 tmux 窗口里, 注册时 daemon 会自动解析 pid → tty → pane.
各工具的具体配置片段在 docs/configs/opencode.md (其它在 docs/configs/).
3. 从 agent 里使用
agent 连上 daemon 后, 你不需要去记工具名字. 直接用平时跟 agent 对话的语言告诉它你想干嘛, agent 会自己挑工具 — 下面列的是 你说的话, 不是底层 API.
注意: 这些都要在 agent 会话内说. 不要用
curl或其它外部 HTTP client 去注册或发消息 — 那会开一个不同的 MCP session, 消息送不到你这里.
注册当前会话
agent 第一次连上 xats 时不会自动注册, 要等你开口. 直接说:
Register me to xats as alice.
或者指定 team:
Register me to xats as alice on team backend.
不传 team 的话, agent 会用当前工作目录的 basename 作为默认 team — 一般情况下你不用操心.
跟其它 agent 对话
按名字, 按 team, 按 role 都行:
Send a message to bob: how is the migration going?
Tell my team I'm starting the deploy.
Send the frontend role a heads-up that the API will change.
What's in my inbox?
agent 会自动挑对应工具 (send_message, broadcast, broadcast_to_role, get_inbox). 发消息的同时会自动唤醒收件人, 不用单独再 poke.
看看还有谁在线
Who else is registered on xats?
List agents on team backend.
4. 跨主机 / 跨设备协作
大部分用户只用单机就够了, loopback 场景下 device 这个轴是透明的, 本节可以完全跳过. 只有当你想让多台物理机器 (LAN, tailscale 等) 共享一个 daemon 时, 才需要往下看.
跨设备需要三处配套修改 — daemon bind, 远端 .mcp.json, agent 注册. agent 身份按 (device, team, name) 命名空间区分: 裸的 send_message({to_agent_name:"creator"}) 解析到调用者自己的 device, 用 creator:host-b 可以指到另一个 device 上同 team 的 agent.
1. Daemon 侧: bind 到非 loopback
停掉旧 daemon, 用非 loopback --host 和 --token 重启. --host 非 loopback 时 --token 必填, 否则 daemon 拒绝启动 (token_required_for_non_loopback_bind). --device 可选, 不传则从 daemon 主机的 hostname 派生 (小写 + 非 [a-z0-9_-] 替换为 -):
npx -y cross-agent-teams-mcp@latest daemon \
--host 0.0.0.0 \
--port 9100 \
--token "$XATS_TOKEN" \
--device host-a想限定监听接口, 把 0.0.0.0 换成具体 LAN IP (例如 10.0.0.10) 或者 tailscale CGNAT IP (100.x.x.x) 都行. macOS 第一次绑非 loopback 端口会弹"允许 node 接受网络连接", 选允许.
2. 远端机器侧: 改 .mcp.json
每台远端同事的 Claude Code 相对默认 loopback 配置都要改两处 — HTTP 入口加 Authorization: Bearer … 头, channel proxy 加 --token 和 --device.
--device对跨主机场景是关键配置. daemon 端会拒掉任何不带 device 的远程register_agent(返回device_required_from_remote), 因此 channel proxy 缺--device时会陷入 register/fail/respawn 死循环, 永远叫不醒目标 agent — auto-poke 会静默退化成no_pane. v0.5.18 起 proxy 在 daemon 非 loopback 且未传--device时会用os.hostname()自动派生一个 label 并 stderr 打 notice, 但派生值仍可能与 daemon 本机标签撞 (触发device_spoofing_local_label_from_remote), 跨主机部署务必为每台机器在配置里显式钉死--device:
{
"mcpServers": {
"cross-agent-teams": {
"type": "http",
"url": "http://10.0.0.10:9100/mcp",
"headers": {
"Authorization": "Bearer xats"
}
},
"cross-agent-teams-channel": {
"command": "npx",
"args": [
"-y", "-p", "cross-agent-teams-mcp@latest",
"cross-agent-teams-channel",
"--daemon-url", "http://10.0.0.10:9100/mcp",
"--token", "xats",
"--device", "host-b"
]
}
}
}如果远端用的是 Codex CLI, 改 ~/.codex/config.toml:
[mcp_servers.cross-agent-teams-mcp]
url = "http://10.0.0.10:9100/mcp"
bearer_token_env_var = "XATS_TOKEN"启动前 export XATS_TOKEN=xats.
daemon 所在机器 (host-a 这台) 的 .mcp.json 同样需要加 headers.Authorization — daemon 一旦设了 --token, 所有 /mcp 请求 (包括 loopback) 都要带 token, 没例外.
3. Agent 注册
重启远端的 Claude Code (或 codex), channel proxy 用新的 --device 启动后, startup hint 会把 device 直接嵌进引导文案, 用户回复时一并带上即可:
Register me to xats as alice, device host-b.
如果远端 register_agent 不传 device, daemon 回 device_required_from_remote 直接拒. device 进入身份键 (device, team, name), 所以两台机器都可以有 team=default 下的 creator, 不会撞名.
4. 跨设备寻址
注册完成后, 用 name:device 后缀寻址同 team 不同 device 的 agent:
Send creator on host-a a message: build is green.
这条解析成 creator:host-a, 路由到 (device=host-a, team=…, name=creator) 这一行. 裸名字 creator 始终解析到 caller 自己 device.
要点:
list_agents每条返回都有device字段, 用它看清 team 里哪些 device 在贡献 agent, 再拼对的name:device.get_inbox每条消息都带from_name和from_device. 回复时如果from_device !== 自己 device, 用from_name:from_device; 同 device 用裸名即可.send_message_by_id({to_agent_id: from_agent_id, ...})是 device 无关的安全兜底.- 安全提醒: bearer token 在能连到 daemon 的所有人之间共享, 把 LAN 暴露当作可信团队边界处理 — 本模式没有 per-agent 鉴权, 没有 device 白名单, 也没有 TLS.
- 升级说明: 引入
device轴之后首次启动会自动迁移存储 schema, 把身份从(team, name)改为(device, team, name), 并用 daemon 本机的--device标签回填旧数据. 如果已经注册了多个 device 上相同(team, name)的 agent 再回滚, 可能违反旧版本的唯一性假设.
5. 跨设备场景下 Codex 特有的坑
--token + Codex --remote 模式下会暴露三个本地单设备 setup 看不到的问题:
app-server 的 env 在启动时固化.
codex app-server --listen ...继承启动它那个 shell 的环境. 你在另一个 shellexport XATS_TOKEN=…之后, 已经在跑的 app-server 看不到 —— codex MCP 握手时报Deserialize error: data did not match any variant of untagged enum JsonRpcMessage(codex 把 daemon 返回的 401 body 当 JSON-RPC 帧解析失败). 解决: 在已经export好XATS_TOKEN的 shell 里重启 app-server.--remote会劫持工作目录.codex --remote …下 session 的 cwd 是 app-server 进程的 cwd, 不是 TUI 的, 所以 launcher 无论在哪个目录跑都会落回 app-server 启动时的目录. 在codex命令上加-C "$PWD"覆盖 (上面 launcher 已经带了).项目级
.codex/config.toml会覆盖全局. 陈旧的 per-project 配置块 —— 尤其在 iCloud / Dropbox 之类跨机同步的目录里 —— 会盖掉你的全局鉴权设置, 报错形如某个codex mcp list(只反映全局) 里看不到的 server 名启动失败. 审计:find ~ -path '*/.codex/config.toml' -print, 删掉或更新陈旧条目.
更多
- 完整工具列表和参数: 启动 daemon 后调 MCP endpoint 的
tools/list. - 各 agent 详细配置:
docs/configs/. - 源码: github.com/jtianling/cross-agent-teams-mcp.
