MCP 工具 Schema 设计:让模型稳定调用,而不是猜参数
MCP 工具的输入输出 schema 是模型、客户端和业务服务之间的长期契约。本文讲解参数设计、错误模型、分页、兼容性和可测试性,避免工具越多越难用。
本文要点
- 主题聚焦:AI、AI Agent、MCP
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
一个工具能否被模型正确调用,往往不取决于描述写得多华丽,而取决于 schema 有没有把业务边界说清楚。参数名含糊、可选字段过多、输出只有一段自然语言,都会让模型在多轮任务中不断猜测。生产级 MCP 工具应该被当成公开 API 来设计。
从一个任务而不是一个后端接口开始
后端常见接口会把所有字段暴露出来,例如 update_ticket(id, body)。模型看到这种接口时,不知道 body 能改哪些字段,也不知道哪些修改需要确认。更合适的拆分是围绕用户任务:get_ticket、add_ticket_comment、change_ticket_priority。每个工具只做一件可审计的事。
工具名使用动词加对象,避免 doAction、run 这类泛化名称。描述里写清楚前置条件、影响范围和是否会写入数据。模型不能可靠地从名字推断风险,所以“会创建外部资源”或“只返回汇总数据”这样的语义必须明写。
输入参数要少而强约束
优先使用枚举、明确的字符串格式和有限的对象层级。例如优先级不要接受任意字符串:
{
"ticketId": "INC-1042",
"priority": "high",
"reason": "Customer-facing checkout is unavailable"
}
其中 priority 应限定为 low、medium、high、critical,而不是接受“紧急”“P0”“高”之类的自然语言。用户输入仍然可以是自然语言,但客户端或工具前的解析层负责归一化;工具 contract 不应承担模糊解释。
必填字段只保留完成任务不可缺少的部分。把可从会话、授权上下文或当前项目推导出来的信息移出参数,例如用户 ID 和 tenantId 不该让模型填写。参数越多,模型漏填、填错或跨租户猜测的概率越高。
输出同时服务模型和人
工具输出不要只返回“成功”。至少包含稳定的机器字段、用户可读摘要和下一步可用信息:
{
"ticket": { "id": "INC-1042", "priority": "high", "status": "open" },
"changed": ["priority"],
"summary": "已将 INC-1042 的优先级更新为 high。",
"nextActions": ["add_ticket_comment", "get_ticket"]
}
机器字段让下一次工具调用可以引用精确 ID,摘要让用户确认实际结果,nextActions 则降低模型猜测可用工具的成本。对于查询工具,返回结构化结果、分页游标和总数范围,而不是把几千行表格拼成一段 Markdown。
错误是 contract 的一部分
把错误分成至少四类:输入无效、没有权限、资源不存在、可重试的依赖失败。每类错误都应给模型一个稳定 code 和可行动建议:
{
"code": "SCOPE_REQUIRED",
"message": "The current session cannot export analytics data.",
"retryable": false,
"requiredScope": "analytics.export"
}
不要把数据库错误、堆栈或下游 HTML 原样返回。它们既不利于模型恢复,也可能泄露内部实现。对于限流和暂时性故障,要告诉调用方何时重试,而不是让模型连续重放同一个写操作。
演进时保持兼容
已发布的工具字段不能随意改名或改变含义。新增字段可以先做可选,并提供默认行为;废弃字段要保留一个迁移周期,在描述和响应里标明替代方案。破坏性变化使用新的工具名或明确版本,而不是悄悄改变旧 contract。
每个工具至少要有三类测试:schema 校验测试、授权边界测试、端到端示例测试。把成功样例和典型失败样例放进回归集,模型升级、描述改写或后端改造后都重新跑一遍。稳定的 schema 不是文档装饰,而是 MCP 生态能持续扩展的基础设施。
延伸阅读
Related