基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
  • 知识卡片碎片时间过考点
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
    • AI 热点每日 AI 资讯与动向
AI 工作台
简历
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-11-08 11:24

393个工具背后的MCP接口设计经验

dev.to · AI#MCP#智能体#接口设计
Editor brief · 编辑速览

作者结合拥有 393 个工具的 MCP 服务,讨论工具命名、元数据和能力边界设计。项目用覆盖矩阵标注已支持、主动排除及待实现的接口,并允许智能体在执行前查询能力是否可用。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

393 个工具交给模型,难点已经不只是接口能不能调通,而是模型会不会选错、重试后会不会重复发送、参数拼错后会不会绕过检查。Conduyt 增长负责人 Jordan Tate 用 conduyt-mcp 4.105.7 的实现说明:大型 MCP server 的可靠性,很大一部分来自命名、元数据和提前拒绝错误。

这篇值得读的地方,是把这些约束落到了注册代码、输入 schema 和 HTTP 重试策略里。不过,它给的是工程实践及包内统计,没有证明这些设计能让任务成功率提高多少。

工具越多,越要先告诉模型哪些事做不了

393 个工具不等于覆盖了全部 API。版本 4.105.7 的覆盖矩阵列出 1,162 个 REST 端点:347 个已被工具覆盖,355 个明确排除,460 个计划支持。端点数和工具数是两种统计口径,原文没有进一步解释映射关系。

明确排除的能力包括未认证的公共接口、只能在浏览器完成的 OAuth 流程,而且每项都写了理由。

他们还把覆盖矩阵做成了 conduyt_mcp_coverage。模型通过 conduyt_api_catalog 找到某项能力后,可以先确认当前 MCP 是否支持执行,再开始任务。配合用于解析名称和 ID 的 conduyt_resolve_names,发现能力和实际操作之间多了一步核实。

命名也服务于选择。全部工具采用 conduyt_ 加 snake_case,常见操作使用一致的动词,例如 conduyt_list_deals、conduyt_list_tasks。集成工具则按产品分组,如 conduyt_zoom_test 和 conduyt_mailchimp_push_now。多个 server 同时接入时,前缀还能减少 list_contacts 这类名称冲突。

但名字只能表达大概。描述还要告诉模型何时该换工具。下面这段说明区分了导出文件与读取少量数据:

Writes `out_path` (an absolute path; an existing file is refused unless
`overwrite: true`) and returns only { path, rows, bytes, parts }: the CSV
content is NEVER returned to you (it can be 50 MB). For a small read use
conduyt_list_activities instead.

要看最后一句:小规模读取应使用 conduyt_list_activities。前面则明确 conduyt_export_activities_csv 只返回文件信息,不把可能达到 50 MB 的 CSV 塞回模型。同样,conduyt_pipeline_health_fix_deal 处理四种健康检查,另外两种交给 conduyt_pipeline_health_fix_next_action。

工具注解要按真实副作用填写

全部 393 个工具都有唯一的 title,方便用户审批调用。按原文引用的规范,显示优先级是 title、annotations.title、name。

更关键的是四个行为提示:

  • readOnlyHint 表示不修改环境,默认 false。
  • destructiveHint 表示可能破坏性更新,默认 true,只对非只读工具有意义。
  • idempotentHint 表示相同参数重复调用不会增加效果,默认 false,同样主要描述写操作。
  • openWorldHint 表示可能与外部开放实体交互,默认 true。

缺失注解时,一个查询工具也可能被客户端按有破坏性、不可幂等的写操作处理。Conduyt 因此把标题和提示集中管理,并让缺行的工具在启动注册时直接报错。

这段代码展示了集中维护与强制检查如何结合:

const row = (title, readOnlyHint, destructiveHint, idempotentHint) => ({
  title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint: false,
});
const ro      = (title) => row(title, true,  false, true);
const create  = (title) => row(title, false, false, false);
const update  = (title) => row(title, false, false, true);
const destroy = (title) => row(title, false, true,  true);

export const TOOL_ANNOTATIONS = {
  conduyt_list_contacts:  ro("List Contacts"),
  conduyt_create_contact: create("Create Contact"),
  conduyt_update_contact: update("Update Contact"),
  conduyt_delete_contact: destroy("Delete Contact"),
  // ...389 more
};

export function annotateRegistrations(server) {
  const original = server.tool.bind(server);
  server.tool = (name, ...rest) => {
    const a = TOOL_ANNOTATIONS[name];
    if (!a) throw new Error(`tool ${name} has no row in src/tool-annotations.ts: add a title and its four MCP hints`);
    const registered = original(name, ...rest);
    const { title, ...hints } = a;
    registered.title = title;
    registered.annotations = { title, ...hints };
    return registered;
  };
}

关键是 if (!a) throw,以及必须在工具模块运行前安装包装器。四个辅助函数也不能代替逐项判断:他们有 44 行手工设置提示,并为特殊选择补充理由。

例如,conduyt_update_deal 重复执行相同 PATCH,仍会再次触发 deal.updated 并写入审计记录,因此不幂等。conduyt_update_calling_hours 关闭拨打时间检查后,会重新允许任意时间联系客户,因此算破坏性操作。导出 CSV 虽然读取 CRM,却会写入甚至覆盖本地文件,因此不算只读。

最终统计为:168 个只读工具、61 个破坏性工具、295 个幂等工具,其中包括 168 个读取和 127 个写入。

重试、分页和参数校验要形成执行约束

幂等提示只描述行为,防止重复写入还得靠服务端机制。

conduyt_send_sms 提供可选的 idempotencyKey,长度为 8 到 200 个字符。发送结果未知时,复用同一个 key,服务端返回原消息而非再次发送。但它的 idempotentHint 仍为 false,因为调用者可以不传 key。

conduyt_pipeline_health_fix_next_action 则通过 Idempotency-Key 请求头传递 key。描述明确区分结果:同 key、同请求体重放首次任务;同 key、不同请求体返回 422;409 表示仍在处理或 key 已被消耗;503 会提示应换 key 重试还是先核实状态。具体判定规则,原文没有展开。

底层 HTTP 客户端只自动重试 GET,最多重试 2 次,写操作不自动重试。写入超时时,错误会提醒模型:服务端可能已经执行,应先读取当前状态。

资源限制同样进入 schema:pipeline health 每页上限 100,批量操作页上限 50,从保存的列表挑选联系人加入序列时上限 500。conduyt_list_contacts 默认每页 25,文档上限 100;翻页看 has_more,不要依赖可能延迟计算或缓存的 total。响应默认限制为 5 MB,超限后提示缩小查询或分页。工具目录 tools/list 本身也通过 cursor 和 nextCursor 分页。

权限沿用 API key:缺少带 cdy_ 前缀的 key,server 拒绝启动;实际调用按该 key 的权限执行。conduyt_check_permissions 返回 tier、scopes、限流和用量,工具描述再声明所需 scope,例如 conduyt_create_short_link 需要 messages。

输入校验则防止参数被悄悄丢弃。原文所用 SDK 的非严格对象会移除未知字段,把 expectedUpdatedAt 拼成 expected_updated_at,并发检查可能就没执行。80 个工具因此启用严格 schema,未知字段返回 InvalidParams,对外声明 additionalProperties: false。

我的判断

我最认可的是把遗漏变成明确失败:注册缺注解就报错,参数拼错就拒绝,响应过大就指导分页。相比只在描述里要求模型谨慎,这些约束更容易检查,也不依赖模型每次都读懂提醒。

但几处判断不能照搬。

全部工具设置 openWorldHint: false,理由是操作域限定在 key 对应的 CRM 账户。作者自己也承认,发送短信是否已经触及开放世界,存在争议。账户范围固定,不代表副作用只留在账户内部。

注解也不能承担权限控制。原文引用的规范明确提醒:注解可能无法忠实描述行为,不应依据不可信 server 的注解决定工具使用。真正的权限仍要在执行端落实。

另外,只有 80 个工具启用严格输入,不能据此认为所有 393 个工具都具备同样的参数保护。启动失败适合暴露开发遗漏,但大型团队还需要在发布前发现问题,避免上线时才撞上注册错误。

作者没有提供命名调整前后的选错率、开启覆盖查询后的调用轮数,或不同工具组织方案的对照。这些建议有明确的故障机制支撑,效果幅度仍属于经验之谈。

能拿来做什么

  • 给 Node MCP server 建立统一的工具元数据表,并在注册时检查标题和四个提示是否齐全;涉及事件、审计和文件写入的工具,要逐项核对实际副作用。
  • 给 AI 编程流程中的发送和创建操作设计幂等 key,同时写清结果未知后的查询步骤;不要让通用 HTTP 重试逻辑直接重放写请求。
  • 检查会删除数据或携带并发条件的输入 schema,确保未知字段明确报错,并为分页、批量数量和响应体积设置上限。
  • 为容易混淆的工具补充返回内容、所需权限和替代工具;能力较多时,再提供可查询的覆盖清单,让模型先确认任务能否完成。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
下一篇
VIDRAFT 开放180B推理模型权重

资讯速览

dev.to · AI · 2026-11-08 11:24

AI 评分 8.0 / 10

相关标签

#MCP#智能体#接口设计

继续阅读

  • VIDRAFT 开放180B推理模型权重
  • 让编程智能体用本地语音考查代码理解
  • 用统一Schema抽取多站点商品数据