mcpField
Z
zoanthrope

zoanthrope

本地服务
github.com/spuentespAI 与智能体02 周前更新

关于 zoanthrope

zoanthrope 是面向协作 Agent 群组的 MCP 协调服务,管理文件读写声明、在线状态、任务意向和审计记录。它结合文件内容指纹与文件系统租约,减少 Agent 同时修改共享文件造成的冲突。

主要功能

  1. 01管理文件读写声明与冲突
  2. 02登记 Agent 在线状态
  3. 03发布并更新任务意向
  4. 04使用文件系统租约防止覆盖
  5. 05记录审计事件并支持重新加入

适用场景

  1. 01协调多个 Agent 修改同一工作区
  2. 02确认文件持有者及声明用途
  3. 03Agent 重启后恢复协作状态
  4. 04排查过期声明和文件冲突

MCP 相关

通过 stdio JSON-RPC 提供 MCP 服务,暴露文件声明、Agent 状态、意向公告、重新加入和审计等工具。另提供用于声明、释放、恢复和查看状态的 CLI。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"spuentesp-zoanthrope": {
"command": "npx",
"args": [
"-y",
"@spuentesp/zoanthrope"
]
}
}
}
启动命令npx -y @spuentesp/zoanthrope

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@spuentesp/zoanthrope0.1.6

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

zoanthrope

传递蜂巢意志、指挥虫群的泰伦虫族生物—— zoanthrope 是协调中继:谁在哪儿工作、承诺了什么、发生了什么。

一款虫群协调 MCP 服务。 zoanthrope 为协作 Agent 的工作区解答一个问题——谁在做什么?——它支持带有读取/编辑意图的文件认领、Agent 状态、公开的意向公告板、针对每个路径的文件系统租约以防止相互覆盖、供离开后又回来的 Agent 使用的重新加入流程,以及有容量上限的审计环形日志。它让一组 Agent 避免互相妨碍,并留下谁承诺了什么的记录。

它是什么

  • 一个通过 stdio JSON-RPC 运行的小型 MCP 服务(zoanthrope mcp)
  • 一个面向钩子的命令行工具(zoanthrope claim / release / rejoin / status)
  • .zoanthrope/state.json 中的状态注册表,以及 .zoanthrope/locks/ 中的文件系统租约——无需守护进程,无需安装和保持运行

工具接口

所有工具都接收并返回 JSON 对象。成功响应体包含 "ok": true;被拒绝的认领是一次成功调用,其响应体包含 "ok": false 和冲突结构。

工具参数返回值
claim_filesagent_id、mode(claim | release | list,默认 claim)、files(路径或 {path, purpose?})、purpose?、intent?(默认 edit | read)、stamps?(由调用方固定的 {name, value} 数组)认领:{ok, granted: [{path, claimed_at, purpose, intent, caller_stamps}]} 或冲突结构;释放:{ok, released: [path]};列表:{ok, claims}
list_claimsagent_id?{ok, claims: [{path, agent, intent, claimed_at, purpose, held_by_stale, caller_stamps}]}
who_am_iagent_id{ok, agent_id, name, kind, last_seen, stale, claims, posts}
list_active_agentsinclude_stale?{ok, agents: [{agent_id, name, kind, last_seen, stale, claims_count}]}
register_agentname、kind?{ok, agent_id, name, kind, last_seen}
post_intentionagent_id、title、note、files?{ok, post_id, post: {post_id, agent_id, title, files, note, status, created_at, updated_at}}
list_postsagent_id?、status?{ok, posts: [...]},按更新时间从新到旧排列
update_postagent_id、post_id、status?(open|taken|done|dropped)、title?、note?、files?{ok, post_id, post}
remove_postagent_id、post_id{ok, post_id, removed: true}
rejoin_agentagent_id、files?{ok, agent_id, claims_kept, claims_at_risk, view_epoch, board}
get_audit_loglimit?(默认 200)、since_unix?{ok, events: [{ts, agent, action, path?, purpose?}]}
冲突结构

这是产品的核心:与其他 Agent 已记录意图冲突的认领会被整体拒绝,并明确指出造成冲突的对象:

{"ok": false, "holder": "beta", "path": "a.rs", "claimed_at": 1791780567.1, "purpose": "fix the parser", "holder_stale": false, "intent": "edit", "caller_stamps": [{"name": "symbol:parse_token", "value": "…"}]}
声明语义

声明携带一种意图(read 或 edit):

  • 一项 edit 声明会与其他 Agent 在该路径上的任何声明冲突——同一时间恰好只能有一个写入者;
  • 一项 read 声明会与其他 Agent 的 edit 声明冲突,但多个读取者可以顺利共持声明;
  • Agent 在某条路径上记录的意图是它持有的最强意图——之后较弱的声明不会隐藏较强的声明。只有先释放再重新声明,才能降级。

每次调用中的声明要么全部成功,要么全部失败:批量操作中只要有一项冲突,就不会授予任何声明。持有者超过在线状态 TTL 没有回应时会被标记为 stale,其声明会标记为 held_by_stale,而且仍会阻止其他操作——声明绝不会自动释放;是否强制解除由策略决定,本产品不负责制定此策略。

声明还会绑定文件的内容:在声明(或重新声明)时记录内容指纹——始终记录 mtime+size,对于 ≤ 256 KiB 的文件,还会记录 sha256,因此即使在时间戳精度较低的文件系统上,同尺寸重写也能被发现。每次受保护的写入(整个 hook 系列)都会先根据声明的指纹检查文件:如果文件在声明期间发生变化(例如被外部追加内容或运行了生成器),写入将被拒绝,并返回 stale-claim 拒绝信息(持有者为自身),提示 Agent 重新读取并重新声明。重新声明会将指纹重新绑定到当前内容;release 会将其丢弃。不存在有意接管:声明锁是一项内容承诺,而不只是写入者锁。

声明还可以携带调用方标记:由你自行计算的不透明 {name, value} 对,例如符号指纹、哈希等,可通过 claim_files 上的 stamps 或 CLI 中的 --stamp name=value 附加(可重复指定)。zoanthrope 会存储调用方标记,并且只重新检查自身的文件标记;验证调用方标记是调用方的职责。授予声明、冲突信息(等待中的 Agent 可以看到持有者固定的标记)、list_claims 和 who_am_i 都会返回这些标记。通过标记重新声明会直接替换已存储的标记集合;不带标记重新声明则会保留现有标记;release 会随声明一并丢弃这些标记。

文件系统租约(防止覆盖)

成功授予的 edit 声明还会在 .zoanthrope/locks/ 下获取文件系统租约:每条路径对应一个以随机数命名的哨兵文件,并通过短暂的 O_EXCL 守卫获取(确保并发获取者无法同时通过持有者扫描),通过移至旁处再验证的方式释放(确保延迟的释放操作绝不会删除替代持有者的文件)。即使注册表中没有记录,仍会拒绝有其他持有者的有效租约——仅持有租约的写入者(编辑器保存操作、意外进程)仍能独占该文件。Read 声明不获取租约。

租约带有心跳 mtime;超过 60s 的租约(可通过 ZOANTHROPE_LEASE_TTL_SECS 覆盖)会被视为过期,并可通过移至旁处再验证的方式接管。status 会显示租约层信息,便于发现遗留的哨兵文件。

公告板

公告是 Agent 的公开意图——调试 Agent 群组时,人类可以直接阅读的一行文字。post_intention 会新建一条公告(status: open);update_post 会将其依次更新为 taken → done,如果计划有变则更新为 dropped。公告不会互相替代;一个 Agent 可以同时持有多条公告。只有发布公告的 Agent 才能编辑或删除自己的公告。

重新加入

离开后又重新上线的 Agent(重启、休眠、断开连接)会调用 rejoin_agent。该操作会刷新在线状态,重新确认 Agent 自己的声明(刷新或重新获取文件系统租约),重新发布公告板,并报告状态——绝不会重写数据。如果注册表仍将某项声明记在调用方名下,但此时另一 Agent 持有该文件的有效租约,该声明会以 claims_at_risk 返回:只报告,不会夺回,也不会释放。重新加入绝不会破坏任何声明,无论是自己的还是其他人的声明。

快速开始

$ cargo build --release
$ ./target/release/zoanthrope mcp

然后,通过 stdio(每行一条 JSON-RPC 消息):

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"demo","version":"0"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"claim_files","arguments":{"agent_id":"alpha","files":[{"path":"src/a.rs","purpose":"fix the parser"}]}}}

让另一个 Agent 指向同一文件,其调用会返回上文所示的冲突结构,其中 "holder": "alpha"。

预编辑钩子使用 CLI 镜像,而非 MCP(参见 hooks/):

$ zoanthrope claim --files a.rs,b.rs --agent alpha --purpose "fix bug" --intent edit
$ zoanthrope release --files a.rs,b.rs --agent alpha
$ zoanthrope rejoin --agent alpha
$ zoanthrope status [--workspace DIR]

退出码:0 表示已获准/正常,1 表示冲突(或 ok: false),2 表示用法或 IO 错误。--workspace 在任何地方都是可选的;省略时,会从当前目录向上查找 .git 来确定根目录。

接入钩子(Claude Code)

守护钩子系列会认领工具调用将要修改的每个文件,并在阻止模式下将冲突转为工具调用拒绝。这正是运行已记录冲突实验时使用的接线配置——将其复制到工作区的 .claude/settings.json 中(按仓库所在位置调整路径):

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Edit|Write|MultiEdit|Bash",
        "hooks": [{ "type": "command", "command": "/path/to/zoanthrope/hooks/claude-code/claim-guard.sh" }] }
    ],
    "PostToolUse": [
      { "matcher": "Edit|Write|MultiEdit|Bash",
        "hooks": [{ "type": "command", "command": "/path/to/zoanthrope/hooks/claude-code/release-guard.sh" }] },
      { "matcher": ".*",
        "hooks": [{ "type": "command", "command": "/path/to/zoanthrope/hooks/claude-code/watch.sh" }] }
    ]
  }
}

每个 Agent 都需要在环境中设置自己的身份(ZOANTHROPE_AGENT=alpha,……)——有关身份解析顺序以及如何将钩子移植到其他 Agent 框架,请参阅 hooks/README.md。

**一套接线,两种模式。**建议模式与阻止模式是运行时模式,而非不同的钩子:这套接线适用于两者。claim-guard.sh 会为每次工具调用解析模式,优先级从高到低如下:

  1. ZOANTHROPE_MODE 环境变量(advisory | blocking)——按 Agent 设置的覆盖值,因此一个群组可以阻止其机器人,同时让一个参与者保持建议模式;

  2. 工作区根目录下的 .zoanthrope/config.json(与 CLI 一样,通过向上查找到 git 顶层目录;该文件为可选):

    {"mode": "advisory"}
    
  3. 默认值:blocking(经实验验证的行为)。

在 advisory 模式下,守护钩子会认领文件,将认领/冲突 JSON 打印到 stderr,并始终以 0 退出——绝不会发出 permissionDecision: deny。在 blocking 模式下,冲突会拒绝工具调用:以 2 退出,原因中会指出持有者(保持不变)。无效的模式值会回退到 blocking,并在 stderr 输出一行警告——钩子绝不会崩溃。pre-edit.sh 仍作为旧版建议模式别名;release-guard.sh 和 watch.sh 绝不会拒绝调用,因此模式不适用于它们。

给 Agent 的说明

将以下内容粘贴到群组的提示词或 AGENTS.md 中——这是经冲突实验验证的协议:

A coordination service (zoanthrope) is active in this workspace.
- Before touching shared files, post your intention (`post_intention` /
  `zoanthrope claim --files … --agent <you> --purpose "…"`).
- An edit may be DENIED with a reason naming the holder. That means
  another agent owns the file right now: wait briefly, then retry
  (up to 3 times). If still denied, work on something else and record
  it — never work around the lock (no out-of-band writes).
- Prefer append/insert on shared files; if you must rewrite a file
  whole, read it back after writing and merge anything that appeared.
- Post what you plan to do on the postboard before you start, and
  update the post when done (`update_post`).
- If you return after being away, call `zoanthrope rejoin --agent <you>`
  once — it re-asserts your claims and tells you what changed.

如需咨询和调试(谁持有哪些资源、失效的 Agent、孤立认领),请将 skills/zoanthrope/SKILL.md 提供给你的 Agent。

状态

所有内容都存储在工作区根目录下的 .zoanthrope/state.json 中:认领、Agent、帖子、已发布的状态板(谁持有哪些资源,并附有代数 epoch 标记),以及最多包含 1000 个事件的仅追加审计环。写入是原子操作(临时文件 + 重命名——重命名即发布代数),并由锁文件串行化处理。若状态文件的格式版本不同,程序会拒绝读取并给出说明;将 .zoanthrope/ 移至其他位置即可重新开始。

存在状态的 TTL 默认为 15 分钟;ZOANTHROPE_TTL_SECS 可覆盖此值。

性能

*以纳秒为单位做出决策,以微秒为单位持久化,可扩展至 128 个并发 Agent,且不会丢失写入。*此保证涵盖通过受保护钩子(Edit/Write/MultiEdit 和可识别的 Bash 修改操作)执行的写入路径;无法解析的命令会记录为 unclaimed-mutation,而不会被阻止;生成器脚本 (node script.js)不会自动纳入保护,除非它们通过可识别的惯用方式写入——审计记录仍是安全保障。 所有数据均在此机器上测得;每组数据都可通过所列命令复现。

延迟 — cargo run --release --bin zoo-bench:

路径p50p99
内存中的 claim+release 对0.30 µs0.92 µs
磁盘上 state.json 的 claim+release 对387 µs695 µs
热文件上的 contended-8(修复退避后)355 µs14.5 ms
对 1000 条帖子执行 list_posts3.86 ms—

成本在于持久化,而非决策。contended-8 的 p95 为 7.3 ms;此前采用固定 20 ms 轮询时,同一尾延迟达到 101 ms;现在锁获取采用指数退避,将其降了下来。Postboard 创建速度为每秒 614 条帖子。

覆盖写入混沌测试 — cargo test --release --test clobber_chaos -- --ignored(通过 ZOO_CHAOS_WORKERS 调整规模):

工作线程迭代次数违规数吞吐量已解决冲突
169,6000——
6412,8000588 ops/s9,809
12819,2000591 ops/s16,205

随着群组规模扩大,预防机制依然有效。有一个已记录的限制,明确说明如下: 当写入速度超过约 10⁵/s 时,事后覆盖写入检测器的采样速度赶不上观察速度——锁是预防机制,也是保障;在机关枪般的写入速率下,审计记录只能尽力而为。

使用真实 Agent 进行 A/B 测试 — 3 个并发 Agent 操作共享文件, 加锁组与未加保护的对照组(完整协议和每次运行的数据见 experiments/zoo-ab-2026-10-12.md):加锁组最终 没有丢失任何工作,有 3 次拒绝操作明确指出了占用者,均在重试后成功恢复;两组耗时相当(53.1 s 对 52.9 s)。范围应从覆盖情况来界定:通过工具执行的编辑(Edit/Write)依赖宿主 harness 自带的 CAS/OCC——已在 24 次 Agent 运行中验证没有丢失任何工作——因此,这把锁专门针对那些会绕过这些工具的操作:Bash 修改操作、codemod、生成器、脚本。针对这类操作,第 3 轮的表格显示丢失数为 0 对 1;对照组中有 6 次进行中的覆盖写入自动修复。

形式化保证

协调核心通过 formal/ 中的 TLA+ 模型进行验证:

  • PresenceClaims — 占用声明仲裁:不存在冲突的持有者,允许共享读取,并记录最强意图;
  • FsLease / FsLeaseGuard — 文件系统租约互斥,防止覆盖写入的核心机制(通过移至旁处并验证来释放,使用 O_EXCL 获取保护);
  • RejoinProtocol — 返回的 Agent 重新声明占用,且不会破坏自身或他人的占用声明。

通过 formal/check.sh 使用 TLC 验证:11/11 种配置——4 种不变式配置通过;7 种负向对照配置均复现其反例(这些规范确实能捕获其声称能捕获的故障模式:仲裁失效、租约无保护、重新加入时泄漏)。重新验证只需一条命令:

$ cd formal && ./check.sh

(TLA2TOOLS_JAR 指向 tla2tools.jar,TLA+ 工具版本为 v1.8.0;规范到代码的映射见 formal/README.md。)

开发

$ cargo test
$ cargo fmt --check
$ cargo clippy --all-targets -- -D warnings

tests/mcp_smoke.rs 会启动 zoanthrope mcp,并通过 stdio 完整运行 initialize → tools/list → claim → conflict → postboard 往返 → rejoin → release 流程。npm-shim/ 是 @spuentesp/zoanthrope Node 启动器(轻量,不包含下载机制——会转发到 PATH 或 ZOANTHROPE_BIN 中的二进制文件)。

许可证

MIT — 详见 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。