MCP 工具设计笔记:从“能调用”到“可安全执行”
MCP 解决了模型与外部能力之间的连接方式,但协议接通并不代表工具已经适合交给 Agent 使用。
第一次设计工具时,很容易把已有 HTTP 接口直接暴露给模型:接口有几个参数,工具就照搬几个参数。实际使用后会发现,面向程序员的 API 与面向模型的工具契约并不完全相同。模型需要更清晰的使用条件、更小的操作范围,以及可以据此修正下一步的结构化反馈。
1. 工具名称和描述就是路由条件
模型通常先根据名称和描述判断是否调用工具,因此描述不能只写“查询数据”或“更新记录”。它至少要说明:
- 工具适合解决什么问题;
- 什么时候不应该调用;
- 必填参数的业务含义;
- 返回结果能证明什么;
- 操作是否会修改外部状态。
如果多个工具的语义高度重叠,模型就容易随机选择。与其提供十个边界模糊的工具,不如提供少量职责清楚的能力。
2. Schema 要减少无效自由度
工具参数应该尽量使用枚举、明确字段和可验证格式,而不是让模型拼接一段自由文本再由服务端猜测。
例如文件操作可以把 path、encoding、overwrite 分开;查询工具可以明确分页、排序和过滤条件。对于互斥参数,应在说明和服务端校验中同时表达,不能只依赖模型“自觉”。
3. 先区分只读和写操作
我倾向于把工具按风险分层:
- 只读工具:搜索、读取、状态检查,可以自动执行;
- 可恢复写入:创建草稿、生成临时文件,需要返回明确变更;
- 重要修改:覆盖配置、发送消息、发布内容,需要确认目标;
- 破坏性操作:删除、批量迁移、权限调整,需要额外审批和范围校验。
一个名为 manage_project 的万能工具很难设置安全边界。把读与写拆开,既有利于权限控制,也能让执行日志更容易审计。
4. 返回错误要能指导下一步
仅返回 success: false 对 Agent 帮助很小。更实用的错误结构应该包含:
{ "code": "RESOURCE_NOT_FOUND", "message": "未找到目标文件", "retryable": false, "details": { "path": "src/config/app.ts" }}Agent 可以据此判断是修改参数、重新检索,还是停止并请求用户确认。对限流、超时和临时网络故障,还应明确是否适合重试,避免模型在永久错误上反复调用。
5. 写工具要考虑幂等性
Agent 可能因为超时或响应丢失而重复调用。创建订单、发送消息或提交任务等操作,如果没有幂等键,就可能产生重复副作用。
因此写操作最好支持:
- 请求级幂等标识;
- 执行前的目标状态检查;
- 明确的变更摘要;
- 可查询的操作结果;
- 在条件允许时提供撤销路径。
6. 一份实用检查清单
- 工具是否只有一个主要职责?
- 描述能否让模型知道“不该什么时候用”?
- 输入是否可以在服务端完整校验?
- 读写权限是否明确分离?
- 重复调用是否会造成额外副作用?
- 错误是否能指导重试、改参或停止?
- 返回值是否包含可验证的目标和状态?
- 日志能否追踪调用者、参数范围与执行结果?
MCP 工具的价值不只是让模型“连接更多系统”,而是用稳定契约把模型的不确定推理与外部系统的确定执行隔离开。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!












