☰
Relay 项目 MCP Tool Description 编写规范:五要素模板、inputSchema 约束与错误返回标准
2026/10/14 2:57:33 网站建设 项目流程
  • 人工智能
  • AI Agent
  • 多智能体
  • 后端
  • 前端

【免费下载链接】relay

Relay 是一个多智能体协作平台,通过统一的界面进行协作开发、代码审查、任务管理,将多个 AI Agent 组织成一个真正的团队。

项目地址:https://gitcode.com/openJiuwen/relay
点击查看免费下载

本文是 Relay 多智能体协作平台中 MCP Tool Description 规范 的完整技术解读与实践指南。它规定了平台上每一个office_claw_*MCP 工具(协作、记忆、调度、富消息等)在注册时必须遵守的 description、inputSchema 与错误返回标准,直接决定了智能体能否"用对"工具而不是"自己 DIY"导致消息丢失。读完本文,你将掌握五要素 description 模板、zod schema 的书写约束、三段式错误消息规范,以及如何对照审查清单与注册测试(tool-registration.test.js)完成一次合格的 MCP 工具上线。

为什么 description 会成为多智能体平台的"生死线"

Relay 将多个 AI Agent 组织成一个真正的团队,它们通过packages/mcp-server暴露的 MCP 工具与平台交互。MCP 规范只定义了工具的外壳(name/description/inputSchema/ handler),而工具的正确用法完全靠 description 文本传达给模型。

在 Relay 中,description 不是给人看的注释,而是被模型直接消费的路由信号。一次失败的调用会沿着 callback → 出站 pipeline → IM 的链路传播,最终表现为"飞书收不到文件"这类用户可见故障。文档中记录的历史事故正说明了这一点:某工具只写了"生成文档并保存为 RichBlock",没说"什么时候用"、没说"不要手动拼create_rich_block"、没说"产物会自动投递到 IM",结果模型看不到正确用法 → 自己 DIY → 平台侧收不到产物。

因此文档开头就给出了明确态度:写新 MCP tool 或优化现有 tool 时必读,description 五要素"缺一个就是不合格"。

Description 五要素与模板:让模型"知道怎么用"

五要素定义

每个工具 description 必须回答五个问题:

要素回答的问题作用
1. 做什么一句话能力描述让模型判断"这个工具是干嘛的"
2. 什么时候用触发关键词 / 用户常见表述让模型把用户意图路由到正确工具
3. 不做什么 / 不适用场景排除错误路由防止模型把 A 工具当 B 工具用
4. 产物调用后用户会看到什么管理模型对副作用的预期
5. GOTCHA常见陷阱 / 和相似工具的区别阻断已知的错误用法

标准模板

{一句话:做什么 + 对谁有用}. Use when: {用户说"生成报告""导出 PDF""帮我写份文档"等}. NOT for: {不适用场景,和相似 tool 的区别}. Output: {产物描述:文件类型 / 附着方式 / 副作用}. GOTCHA: {陷阱提醒,例如"不要用 create_rich_block 手动拼"}.

好 vs 差的 description 对照

文档给出了同一工具(Markdown 生成文档并投递到 IM)的两版 description:

差(OpenCode 踩坑的根因):

Generate a document (PDF/DOCX/MD) from Markdown content using Pandoc. The generated file is automatically saved and attached to the current message as a file RichBlock.

问题逐一对照:没说"什么时候用"、没说"不要用 create_rich_block 手动拼"、没说产物会自动投递到 IM。结果就是 OpenCode 看不到正确用法 → 自己 DIY → 飞书收不到文件。

好:

Generate a document (PDF/DOCX/MD) from Markdown and deliver to IM platforms. Use when: user asks to "生成报告/导出文档/发PDF/写份文档给我". NOT for: sending existing files (use create_rich_block with kind:"file" + url). NOT for: generating images (use image generation tools instead). Output: DOCX/PDF/MD file saved to /uploads/, attached as file RichBlock, automatically delivered to Feishu and other IM platforms via outbound pipeline. GOTCHA: Do NOT manually call pandoc + create_rich_block — that skips IM delivery. Always use this tool for document generation.

好版本同时覆盖了正路由(Use when)、反路由(NOT for)、产物与副作用(Output)、陷阱(GOTCHA),并把"兜底结论"(Always use this tool)写死,防止模型在多个相似工具间犹豫。

仓库中的落地范例

规范不是空谈,仓库里几乎所有office_claw_*工具都按这个结构编写。以 callback-tools.ts 中的office_claw_post_message为例:

Post a proactive async message to the OfficeClaw chat mid-task in the CURRENT thread (e.g. progress updates, sharing results). To simply @mention another agent at the end of your response, use @agent-name in your reply text instead — it is free and never expires. GOTCHA: This tool uses callback credentials that expire — if it fails with 401, fall back to inline @mention in your response text. GOTCHA: Do NOT use this for routine replies — only for mid-task proactive messages when you need to share something before your response completes.

再看 rich-block-rules-tool.ts 的office_claw_get_rich_block_rules,它把"渐进式披露"策略与 GOTCHA 结合得很典型:

Get the full rich block usage rules (card/diff/checklist/media_gallery/audio/interactive). Call this BEFORE creating your first rich block in a session — it returns the full schema and constraints. You only need to call this once per session; the rules do not change within a session. GOTCHA: Without loading these rules first, you will likely produce invalid block JSON (wrong field names, missing required fields).

这里"什么时候用"被写成Call this BEFORE ... first rich block,"GOTCHA"明确点出跳过后最可能的失败模式。而 schedule-tools.ts 中的office_claw_set_scheduled_task_enabled则示范了"和相似工具的区别"类 GOTCHA——它反复强调"暂停任务用 enabled=false,永久删除才用 remove_scheduled_task",直接把两个极易混淆的工具边界钉死。

inputSchema 规范:把参数约束写进 schema,而不是留给模型猜

六条核心原则

原则做法原因
能枚举就别自由文本format: enum(['pdf','docx','md'])自由字符串模型极易写错,枚举直接消灭一类错误
字段描述带单位/格式"fileSize in bytes"、"ISO 8601 timestamp"消除单位歧义,模型不必猜
禁止额外字段additionalProperties: false防止模型发明不存在的参数
字段名不同义词start_index或offset,只选一个同义词并存会分裂模型记忆
必填标清楚required: ['markdown', 'format', 'baseName']让模型一次调用就带全必填项
describe 写清约束.describe('Display name without extension, e.g. "调研报告"')在参数层面给出示例

仓库中的 zod schema 实践

Relay 的 MCP 工具用zod定义 inputSchema,每个字段都通过.describe()携带约束与示例。看 callback-tools.ts 中office_claw_post_message的 schema:

export const postMessageInputSchema = { content: z.string().min(1).describe('The message content to post'), replyTo: z.string().optional().describe('Optional message ID to reply to'), clientMessageId: z .string() .min(1) .max(200) .optional() .describe('Optional idempotency key for at-least-once delivery de-duplication'), targetAgents: z .array(z.string().min(1)) .optional() .describe( 'Optional explicit target agent IDs (e.g. ["codex","gpt52"]). Merged with @mentions parsed from content. Used for direction rendering in frontend.', ), };

这里每一个 describe 都写清了语义(做什么用)和格式(长度上限、数组元素、示例值)。再看 schedule-tools.ts 的office_claw_register_scheduled_task,它把"枚举 + 单位 + 倾向建议"三件事一次讲透:

trigger: z .string() .describe( 'Trigger config as JSON string. Choose type by intent: ' + 'interval — "every N hours/minutes" repeating from now, e.g. {"type":"interval","ms":7200000}. interval.ms must be >= 10000 (10s); ' + 'cron — specific wall-clock times/days, e.g. {"type":"cron","expression":"0 9 * * *","timezone":"Asia/Shanghai"}; ' + 'once — fire once after delay or at exact time. PREFER delayMs (relative) over fireAt (absolute) because model clocks may drift, e.g. {"type":"once","delayMs":120000}. delayMs must be >= 1000 (1s); fireAt must be a future epoch ms. ' + 'PREFER interval over cron when user says "every N hours/minutes".', ),

这条 describe 同时体现了三条规范:单位明确(ms、>= 10000)、示例具体(给出了三种 type 的完整 JSON 示例)、最佳实践内置("模型时钟可能漂移,优先相对时间")。

枚举约束也有大量实例,例如任务状态status: z.enum(['todo', 'doing', 'blocked', 'done'])(callback-tools.ts)、workflow 阶段stage: z.enum(['kickoff', 'impl', 'quality_gate', 'review', 'merge', 'completion'])(callback-tools.ts)、检索模式mode: z.enum(['lexical', 'semantic', 'hybrid'])(evidence-tools.ts)。能枚举就别自由文本在 Relay 中已被严格执行。

describe 与运行时校验的配合

schema 不只是给模型看的说明书,它还直接参与运行时校验。office_claw_post_message在 handler 里用postMessageRuntimeInputSchema.safeParse()对归一化后的输入做二次校验,失败时把 zod 的每个 issue 拼成错误返回(callback-tools.ts):

const parsedInput = postMessageRuntimeInputSchema.safeParse(normalizedInput); if (!parsedInput.success) { const issues = parsedInput.error.issues .map((issue) => `${issue.path.join('.') || 'input'}: ${issue.message}`) .join('; '); return errorResult(`Invalid input for office_claw_post_message: ${issues}`); }

这套"schema 定义 + describe 说明 + safeParse 运行时校验"的闭环,正是规范落地到代码的完整形态。

错误返回规范:三个信息缺一不可

规范要求错误消息必须包含:

  1. 哪个字段错了
  2. 期望的类型/范围
  3. 一个正确示例

文档中的对照示例:

// 差 return errorResult('Invalid format'); // 好 return errorResult('Invalid format "xlsx". Expected one of: pdf, docx, md. Example: format="docx"');

坏消息只有"Invalid format",模型完全无法自救;好消息同时给出实际值(xlsx)、合法集合(pdf/docx/md)、修复示例(format="docx"),模型拿到后可以直接改正重试。

仓库里几乎每个 errorResult 都遵循"实际值 + 期望 + 示例"的结构。看 schedule-tools.ts 的validateTriggerConfig——对非法triggerJSON 逐项给出可操作提示:

Invalid trigger JSON — interval trigger ms must be a finite number >= 10000 Invalid trigger JSON — once trigger requires either delayMs or fireAt Invalid trigger JSON — unsupported trigger type: ${type}

file-tools.ts 定义的errorResult()统一以isError: true标记返回,供上层判断失败;而 handler 内联的错误分支(如Access denied: ${filePath} is not within allowed directories)同样遵循"字段/路径 + 期望边界"的结构。规范的另一条隐性要求也在代码中体现:错误要可路由、可重试——模型必须能从报错里推断出下一步。

审查检查清单:上线前的九项硬检查

写完或改完 MCP tool 后,逐项对照:

  • description 有"做什么"
  • description 有"什么时候用"(含用户常见表述 / 中英文关键词)
  • description 有"不做什么 / 不适用"
  • description 有"产物 / 副作用"
  • description 有 GOTCHA(和相似 tool 的区别)
  • inputSchema 参数都有.describe()
  • 枚举值用enum不用自由文本
  • tool-registration.test.js已更新(EXPECTED_TOOLS / EXPECTED_COLLAB_TOOLS)
  • SystemPromptBuilder MCP_TOOLS_SECTION 已更新

前七项是描述与 schema 本身的品质检查,后两项是注册与提示词层的联动,在 Relay 中有明确的源码落点。

注册测试:EXPECTED_TOOLS 是回归守门员

tool-registration.test.js 定义了EXPECTED_TOOLS全量清单与EXPECTED_COLLAB_TOOLS(协作面)清单,测试通过createServer()后比对server._registeredTools,既有"每个预期工具都已注册"的正向断言,也有"不允许出现预期之外工具"的反向断言。文件注释记录了这条规则的由来:request_permission/check_permission_status的 handler 和 schema 早已存在,但createServer()漏了server.tool()注册——正是靠这个"守住注册层"的回归测试,修复前 Red、修复后 Green。所以规范要求新增工具必须同步更新这两个清单,否则全量测试会直接失败。

另一个值得注意的联动是 server-toolsets.ts:其中维护了COMPACT_DESCRIPTIONS紧凑版描述表,供 GLM-5 196K 这类窄上下文模型使用——因为 OpenAI ChatCompletions 会把工具定义序列化进prompt_length,完整五要素描述会浪费大量 token,compact 模式只保留每个 description 的第一句。这说明五要素是"完整形态"而非唯一形态,规范还要考虑不同模型的上下文预算。

SystemPromptBuilder:MCP_TOOLS_SECTION 的单一真相源

SystemPromptBuilder.ts 中的MCP_TOOLS_SECTION是注入每个智能体系统提示词的 MCP 工具摘要,按"记忆工具(先搜后问)→ 记忆 drill-down → 协作工具 → 共享 Skills"分块列出,并标注关键路由规则(如office_claw_multi_mention必须先搜后问、需 searchEvidenceRefs 或 overrideReason)。新增工具后不更新这里,智能体的系统提示词里就看不到它——这正是清单第九项的深层原因:工具注册(MCP 层)与提示词可见性(SystemPrompt 层)必须同步,否则工具"存在但不可用"。

规范在 Relay 全链路中的位置

把本规范放在 Relay 的运行链路中看,它的价值会非常清晰:

  1. schema 定义阶段:按五要素 + zod.describe()写全描述与约束;
  2. 注册阶段:server-toolsets.ts通过registerTools()调用server.tool(name, description, inputSchema, handler)注册(server-toolsets.ts),环境变量OFFICE_CLAW_MCP_EXCLUDED_TOOLS支持按名屏蔽工具;
  3. 模型推理阶段:description 作为系统提示词的一部分进入上下文,模型据此路由;
  4. 调用与校验阶段:zodsafeParse做运行时校验,errorResult返回三段式错误;
  5. 回归守门阶段:tool-registration.test.js确认注册完整、无多余工具,SystemPromptBuilder 保持提示词可见性同步。

任何一个环节断裂,都会演变成文档开头描述的那类故障。因此这份规范实际上定义的是 Relay 中 MCP 工具的最低可交付标准——它不是给"写出优雅描述"的建议,而是保证多智能体系统不出错的过程约束。实践中建议把五要素模板作为所有新工具的起点,把审查清单作为合并前的强制关卡,并让注册测试成为"未更新即红"的自动化护栏。

  • 人工智能
  • AI Agent
  • 多智能体
  • 后端
  • 前端

【免费下载链接】relay

Relay 是一个多智能体协作平台,通过统一的界面进行协作开发、代码审查、任务管理,将多个 AI Agent 组织成一个真正的团队。

项目地址:https://gitcode.com/openJiuwen/relay
点击查看免费下载

相关推荐

上一篇:快速提取Android OTA payload.bin的终极指南:为什么payload-dumper-go是你的最佳选择
下一篇:s1 项目集成 lm-evaluation-harness:Unscramble 字符打乱任务评测指南

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询