news 2026/9/29 2:35:24

ZenlessZoneZero-OneDragon MCP Tool 实现规范:绝区零一条龙项目特化的 tool 编写落地指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ZenlessZoneZero-OneDragon MCP Tool 实现规范:绝区零一条龙项目特化的 tool 编写落地指南
  • 桌面应用
  • RPA
  • 计算机视觉

【免费下载链接】ZenlessZoneZero-OneDragon

绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄

项目地址:https://gitcode.com/gh_mirrors/ze/ZenlessZoneZero-OneDragon
点击查看免费下载

本文是对 mcp-implementation.md 的深度展开。绝区零一条龙(ZenlessZoneZero-OneDragon)通过 MCP 适配器把游戏自动化 backend(ZzzBackendContext)暴露给 Claude Code 等 AI 编码工具,本文是该适配器内所有@mcp.tool的「代码怎么写」落地规范:涵盖 annotations 分类标注、Field 参数描述、结构化返回与错误兜底、docstring 三要素、命名、evaluation 缺口与改动同步 checklist。读完本文,你将掌握为本项目新增 / 修改 MCP tool 时逐项对照的完整实现基准,并能结合源码理解每条约束背后的原因。

适用范围:src/zzz_od/backend/mcp/ 下的app.py/service_app.py/prompts.py中所有@mcp.tool。SDK 基线为官方mcp>=2,<3包内置的MCPServer(from mcp.server import MCPServer)。

定位:与设计原则配对的「实现层规范」

本项目 backend 文档体系分三层:design-principles.md是「设计原则」(agent 能力视角,回答为什么这么设计、tool 该不该有),mcp.md/http.md是「现状 spec」(回答现在有哪些能力),而本文是「代码怎么写」的落地规范。三者关系如下:

  • design-principles.md:MCP tool 设计的纲领,从「智能体能做什么 / 做不到什么」推导 tool 该提供什么。其中 P3(操作 vs 观察分离)、P9(docstring 是筛选 prompt)、P11(MCP / HTTP 对称暴露)、P13(观察类可选持久化)、P14(能力边界提示)等原则在本文都有对应的代码级落地。
  • mcp.md:22 个@mcp.tool的工具表现状,本文第 7 节 checklist 的「同步工具表」对象。
  • http.md:并行 HTTP 适配器,是返回结构对称性的比对基准。

通用 MCP tool 设计方法论(tool 命名 / description 契约 / 严格 input·output schema / annotations / 结构化返回 / actionable error / 读写分离 / token 经济)遵循 Anthropic 官方mcp-server-devplugin 的tool-design.md,本文只讲项目特化写法,不重复通用知识。写 / 改 MCP tool 前建议先安装该 plugin,安装方式见 docs/develop/setup/ai_coding.md。

1. annotations:本项目 tool 的分类标注

双标原则(对应 design P3,缺一不可)

  • docstring 首句点明「观察类 / 操作类」——给人 + 模型读的筛选 prompt;
  • @mcp.tool(annotations=ToolAnnotations(...))机器可读——给客户端按副作用筛选 / 自动确认 / 缓存。

两个标注服务于不同消费者(人 / 机器),任何新 tool 都必须同时具备。从 app.py 源码可以看到实际写法,例如:

@mcp.tool(annotations=ToolAnnotations(read_only_hint=True, title="检查游戏窗口")) def check_game_window() -> WindowStatus | dict: ...

本项目三分类

类别read_only_hintdestructive_hint本项目 tool
纯观察(只读,不改状态)True—check_game_window/capture_game_screen/analyze_screen/get_run_status/list_applications/list_operations/describe_operation/list_mcp_usage_guides/get_mcp_usage_guide
操作游戏 / 触发运行 / 改配置(改状态非破坏)不标(默认非 read_only)—click_game/key_tap/drag/input_text/open_game/run_one_dragon/run_standalone_app/run_operation/stop_run/upsert_screen_area
不可逆 / 破坏性不标Truedelete_screen_area(删 screen_info area)/close_game(关游戏)

本项目特化:两个 hint 默认不标

open_world_hint/idempotent_hint默认不标——本项目 tool 都操作本地游戏运行时(属「外部世界」交互):open_world保持 MCP 默认语义即可;idempotent标了也无决策价值(click 幂等无需声明、run 不幂等)。判据只有一个:只在「客户端会因此改变确认 / 缓存策略」时才标。这个「不标」本身也是经过决策的约定,避免为标注而标注。

导入与命名细节

  • 导入:from mcp.types import ToolAnnotations;
  • Python 参数和属性使用 snake_case:read_only_hint/destructive_hint/idempotent_hint/open_world_hint/title;SDK 序列化到 MCP JSON 时自动转成 camelCase(如readOnlyHint);
  • 工厂注册的 tool 同理:mcp.tool(annotations=...)(make_xxx(backend)),见 service_app.py 中make_run_one_dragon/make_list_applications等模块级工厂被 app.py 注册的方式:
    mcp.tool(annotations=ToolAnnotations(read_only_hint=True, title="列出可运行应用"))(make_list_applications(backend))

2. Field 参数描述:哪些参数必须加

背景:MCPServer 不解析 docstring 的 Args 段

MCPServer 把函数签名转成 JSON schema,但不解析 docstring 的Args:段成字段 description——参数级说明只能靠Annotated[type, Field(description=...)]。这是很多 MCP server 实现容易踩的坑:docstring 写得再全,模型在 tool-call 时看到的参数描述来自 Field。

本项目必须加 Field description 的(智能体不靠参数名 + 类型就懂不了的)

  • 布尔开关的隐式语义:save_image/pc_alt/block/enter/use_clipboard——save_image=True会落盘并回传screenshot_path,pc_alt=True需要按住 Alt 解锁光标,use_clipboard=None跟配置走,这些语义仅靠bool类型完全无法表达;
  • 单位 / 默认特殊的:press_time单位是秒(click 默认 0.1 是游戏识别下限、0=极短按可能无效,key 默认 0.0 是短按 tap);
  • 定位符格式:op_id的<module>.<ClassName>格式、screenshot的路径 / 图名规则(纯名字到.debug/images/<名字>.png读);
  • 结构化入参:args的 JSON 可序列化约束(@dataclass+from_dict参数可传 dict,其余复杂数据类走 application)。

不必加:纯坐标(x/y,docstring 已说明是 1080p 游戏空间)、无歧义标量。

分工不重复

  • Field description写「参数是什么 / 取值约束」;
  • docstring写「整体能做什么 / 何时用 / 副作用 / 返回」;
  • 同一条信息只留一处。

官方示例(来自 app.py 的click_game,布尔开关 + 单位特殊的参数加 Field,坐标 x/y 不加):

def click_game( x: float, y: float, press_time: Annotated[float, Field(description="按住时长(秒);click 默认 0.1(游戏识别下限),0=极短按可能无效)")] = 0.1, pc_alt: Annotated[bool, Field(description="点击前是否按住 Alt 解锁光标;大世界等 pc_alt=true 画面必需")] = False, ) -> dict: ...

再比如analyze_screen的screenshot参数,Field 说明了三种取值语义:「None=实时截当前画面(需游戏在线);传路径=读该图(无需游戏在线);纯名字=读.debug/images/<名字>.png」。这种三态语义如果不写 Field,智能体几乎必然传错。

3. 返回值:对称结构与错误兜底

与 HTTP 对称(对应 design P11)

同一 backend 方法,MCP 和 HTTP 返同构字段。例如:

  • check_window→ MCP 返WindowStatusdataclass(定义见 schemas.py)、HTTP/game/window返asdict(WindowStatus);
  • 运行状态三件套对称暴露:open_game↔/game/enter、get_run_status↔/game/status、stop_run↔/game/stop,两侧调同一 backend 方法,跨适配器状态共享同一RunSlot(HTTP 触发的运行 MCP 也能查到)。

别在 MCP 把结构压成多行文本——文档明确指出这是早期check_game_window踩过的坑(已修)。多行文本破坏了结构化返回,模型无法按字段精确消费,也无法与 HTTP 侧对齐。

错误兜底:统一 try/except 返带 error 字段的结构,不 raise ToolError

尽管 SDK 原生支持raise ToolError,本项目统一不采用。理由:不把 opaque traceback 透传给客户端,返回 actionable 结构。两种落地方式:

  1. 成功返回 dataclass 本身带success/error字段→ 错误也返该 dataclass(success=False, error=str(e))。典型:analyze_screen→AnalyzeScreenResult(定义见 schemas.py),错误分支返回AnalyzeScreenResult(success=False, ocr_texts=[], screens=[], error=str(e))。
  2. 成功返回 dataclass 不带错误字段→ 错误返{'error': str(e)}dict,返回类型注解写成T | dict。典型:list_applications→ApplicationListResult | dict、check_game_window→WindowStatus | dict。

源码中每个 tool 都套了统一的工具层兜底,注释写明「工具层统一兜底,避免异常透传到 MCP 框架」,例如 app.py 的check_game_window:

try: return backend.check_window() except Exception as e: # noqa: BLE001 工具层统一兜底,避免异常透传到 MCP 框架 return {'error': str(e)}

单值 ack 例外

close_game的约定文案(「已发送关闭游戏信号」)保持str——结构化无增益。因为它只是「信号已发出」的 ack,真正验证要再调check_game_window。

字段语义化命名

统一使用success/error/in_window/started这类语义化字段,避免 cryptic id。从实际返回结构看:click_game返{success, x, y, in_window, pc_alt, error?},open_game的并发拒绝返回{started, error, source, hint}——hint字段直接告诉智能体下一步该做什么(「先 get_run_status 查状态,或 stop_run 停止」),这就是 actionable error 的落地。

4. docstring 三要素(对应 design P9)

每个 tool docstring 至少包含三要素——design P9 的表述是「docstring 是筛选 prompt」,智能体在众多 tool 里选对工具的依据就是它:

  1. 一句话说能做什么 + 首句标「观察类 / 操作类」:首句标注让智能体在筛选时一眼区分只读与改状态工具,例如check_game_window首句「检查绝区零游戏窗口状态(只读,不改状态)。观察类。」、click_game首句「点击游戏窗口内坐标(1080p 游戏空间,同 screen_info pc_rect 中心)。操作类。」;
  2. 关键约束 / 隐式上下文:坐标空间、需窗口就绪、单跑道、操作后需 sleep等——把智能体猜不到的显式化。例如click_game的 ⚠️ 提醒:「底层 click 无内置等待,点 UI 常触发画面切换(菜单/弹窗/进画面),连续操作或capture_game_screen前建议 sleep ~1s 等动画(否则截过渡帧)」;check_game_window还特别提醒「判游戏是否在跑看is_win_valid,勿据 win_title 判断」——win_title 是 controller 缓存的预期标题,游戏未启动时仍可能非 None。这类「反直觉」约束是 docstring 最有价值的部分;
  3. 返回结构:Returns:段写 dict / dataclass 字段。例如analyze_screen说明「决策优先看screens(精准命中 1 个is_precise=True;否则 top_n 个候选);需要散落文本再看ocr_texts」。

不啰嗦(context 有限),准确说清即可。文档给出的参考基线是新 tool(click_game/key_tap/drag)的密度——「那是踩坑后校准的基线」,后续新 tool 以它们为参照。

5. 命名:资源分组前缀 + 无歧义参数

通用规则(snake_case + 服务前缀 + 动词导向)之上,本项目 tool 按资源分组前缀:

  • game 感知 / 直接动作:check_game_window/click_game/capture_game_screen;
  • 运行:run_*(run_one_dragon/run_standalone_app/run_operation);
  • 查询:list_*/describe_*/get_run_status;
  • screen_info CRUD:upsert_screen_area/delete_screen_area。

参数名无歧义:op_id不写id,use_clipboard不写way。命名空间前缀(design P10)帮智能体在多 server 多 tool 时选对。

6. 借鉴官方 Phase 4 evaluations(本项目缺口,待补)

官方 mcp-builder 把「造 evaluations」作为第 4 阶段:写完 MCP server 后,造10 个只读、复杂、可验证的问题,测 LLM 能否用好它。每题要求:独立 / 只读 / 复杂(多 tool 调用)/ 现实 / 可验证(单一明确答案)/ 稳定。

本项目 MCP tool 目前只有单元测试(mock backend,验证委托与返回结构),缺这套「LLM 好用度」评估——即「智能体光看 tool 描述 / 参数 / 返回,能否选对工具、传对参数、读懂结果」。这正是设计原则 P9 的闭环验证:docstring 是筛选 prompt,但 prompt 写得好不好,只有用真实问题集测过才知道。

后续可针对本项目典型场景(查运行态 / 进游戏 / 分析画面 / screen_info 建模)造问题集,验证描述 / 参数 / 返回是否让 LLM 用得准,反哺本文第 2 / 4 节(Field 描述与 docstring 三要素)。值得注意:仓库内zzz-od-test/test/zzz_od/backend/测试目录属于外部测试仓库(当前仓库不含),文档第 7 节提到的单元测试断言即指该处。

7. 改动同步 checklist:改 / 加 MCP tool 后逐条对照

改 / 加 MCP tool 后逐条对照(原文档完整清单 + 源码佐证):

  • docstring 三要素齐 + 首句「观察 / 操作」标注(见第 4 节);
  • annotations按第 1 节分类标了:观察read_only_hint=True/ 破坏destructive_hint=True,Python 字段使用 snake_case;
  • 读写分离:单 tool 不混观察 + 操作(通用硬要求);相似操作 tool(click_game/key_tap/drag)描述互指何时用另一个(disambiguate)。源码可见click_gamedocstring 写「鼠标点击用本 tool;键盘按键用key_tap;鼠标拖拽用drag」,三个 tool 互相交叉引用,形成 disambiguation 闭环;
  • titleannotation(可选):Anthropic Directory 提交时每个 tool 必须有 title;本项目不提交 Directory,按需作 UI 显示名。当前源码中每个 tool 都带了中文 title(如「检查游戏窗口」「捕获游戏截图」),作为 GUI / 调试的可读显示名;
  • 难懂参数加了Field description(见第 2 节);
  • 返回结构化(非裸字符串,单值 ack 除外)+ 错误兜底按第 3 节;
  • 同步 tool 可在 MCPServer 的工作线程中并行执行:不依赖固定线程;GPU session 继续经gpu_executor串行。这是架构约束——MCPServer 2 会在 AnyIO 工作线程中执行同步 tool,同步 backend 方法不能依赖固定调用线程;OCR / YOLO 仍须通过项目的gpu_executor串行调用(见 mcp.md 与 architecture.md);
  • 同步mcp.md工具表(签名 / 参数 / 返回 / tool 总数)——文档与实现脱节是最高频坑;
  • HTTP 对称能力是否也要补(两个适配器消费者都用 → 放 backend 共享,见 design P11):判断依据——一个能力若两个适配器的消费者都用,就放ZzzBackendContext共享层,MCP 与 HTTP 各自序列化、调同一 backend 方法,别把能力写死在 MCP 适配器;
  • 测试(zzz-od-test/test/zzz_od/backend/)断言对齐新返回;
  • ruff check改动文件 + 经 daemon 重启 server 验证 tool 注册。

附:如何在本项目里跑通一套 MCP tool

理解规范后,实际落地链路如下:

  1. 启动服务:命令行执行uv run python -m zzz_od.backend.entry.server --host 127.0.0.1 --port 23001(项目根目录有.env时用uv run --env-file .env ...,见 entry.md);也可用 GUI「开发工具 -> MCP 服务」页面管理本机 server 子进程(启动 / 停止 / 重启 / 日志.debug/zzz_od_mcp/main_server.log);
  2. 接入 MCP 客户端:MCP 端点http://127.0.0.1:23001/mcp(streamable-http),Claude Code 配置claude mcp add --transport http zzz_od http://127.0.0.1:23001/mcp;Codex GUI 填类型Streamable HTTP、URL 同上(本机接口默认无鉴权,Bearer 令牌留空);
  3. 验证注册:改完 tool 后经 daemon 重启 server(清 Python import 缓存)+ 重连,否则跑的是旧代码——这条来自 prompts.py 的zzz_dev_validate_op开发者指南,是 op 实操验证流程的一部分。

完整工具清单(22 个@mcp.tool的签名 / 参数 / 返回)见 mcp.md;backend 方法与运行槽设计见 architecture.md;服务入口见 entry.md。

  • 桌面应用
  • RPA
  • 计算机视觉

【免费下载链接】ZenlessZoneZero-OneDragon

绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄

项目地址:https://gitcode.com/gh_mirrors/ze/ZenlessZoneZero-OneDragon
点击查看免费下载

相关推荐

上一篇:autojump开源社区冲突解决培训材料:案例与角色扮演
下一篇:workerd 实战:在 samples/nodejs-compat-crypto 中运行 node:crypto 加密模块示例

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 2:34:05

AI PPT生成器实战指南:十分钟搞定毕业论文答辩PPT

周五晚上十一点&#xff0c;隔壁寝室的学弟发来消息&#xff1a;“学姐&#xff0c;答辩PPT能不能借我改改&#xff1f;我还有三页没做完。”我回他&#xff1a;“你先拿Paperzz生成一版&#xff0c;我再帮你捋逻辑。”这不是敷衍&#xff0c;是我这几年帮人改PPT总结出来的工作…

作者头像 李华
网站建设 2026/9/29 2:30:59

Kafka跨集群迁移指南:MirrorMaker2配置、踩坑与消费切换实践

做集群迁移这事&#xff0c;最怕的不是数据量大&#xff0c;而是迁移过程中业务还在跑&#xff0c;存量数据和新写入的数据搅在一起&#xff0c;割接时又发现消费端衔接不上&#xff0c;最后变成一个没法收场的“半迁移”状态。我踩过这个坑之后&#xff0c;遇到跨集群搬迁的需…

作者头像 李华