为企业 MCP 设计可组合工具:从理论到实践
Source: Dev.to
企业 MCP:从 “API 网关” 到基于可组合技能的工具设计
在我之前的文章中,我讨论了 企业 MCP 实施中最大的差距并不是协议本身——而是围绕它的架构决策。具体来说,团队常常把 MCP 当作 “LLM 的 API 网关”,但实际上应该考虑可组合的工具设计。
今天,我想向大家展示可组合、基于技能的工具设计在实践中到底是什么样子。
酒店运营案例研究

让我们从酒店管理系统的真实场景开始。前台员工说:
“Beth Gibbs 正在退房,她说她房间的马桶坏了。”
这段简单的交互需要:
- 处理退房(付款、收据、房间状态)
- 提交维修请求(保持房间上下文)
- 更新库存和可用性
- 将请求路由到合适的维修团队
你会如何为此设计 MCP 工具?
The Naïve Approach
Many (if not most) teams start by exposing existing APIs as MCP tools:
[
"get_guest_by_email",
"get_booking_by_guest",
"get_room_by_booking",
"create_payment_intent",
"charge_payment_method",
"send_receipt_email",
"update_booking_status",
"update_room_status",
"create_case",
"assign_case_to_contact",
"set_case_priority"
]
An agent now has to orchestrate 11+ API calls in the correct sequence, handle potential failures at each step, and maintain state throughout. The result? Slow, error‑prone, and terrible user experiences.
许多(如果不是大多数)团队会先将现有 API 公开为 MCP 工具:
[
"get_guest_by_email",
"get_booking_by_guest",
"get_room_by_booking",
"create_payment_intent",
"charge_payment_method",
"send_receipt_email",
"update_booking_status",
"update_room_status",
"create_case",
"assign_case_to_contact",
"set_case_priority"
]
现在,代理需要按正确的顺序编排 11+ 个 API 调用,处理每一步可能出现的失败,并在整个过程中保持状态。结果如何?速度慢、容易出错,且用户体验 糟糕透顶。
组合式方法
如果我们改为围绕 用户意图 设计工具会怎样?调用可能看起来像这样:
[
"process_guest_checkout",
"submit_maintenance_request"
]
两个工具。一次自然的对话。复杂性并没有消失——它只是转移到了应有的位置。
Source: …
可组合、基于技能的工具设计的九大模式
在实现生产级 MCP 系统后,这些模式决定了优雅架构与脆弱架构的区别。
1. 接受业务标识符,而非系统 ID
错误示例
{
"contact_id": "003Dn00000QX9fKIAT",
"booking_id": "a0G8d000002kQoFEAU",
"room_id": "a0I8d000001pRmXEAU"
}
正确示例
{
"guest_email": "beth.gibbs@email.com",
"room_number": "302"
}
让后端将可读的业务标识符解析为内部 ID。 代理不应需要了解你的数据库模式。
提示: 这适用于 所有 工具参数——不仅仅是主实体。当更新关系(例如,将案件重新分配到其他房间)时,仍然使用业务标识符:
{
"idempotency_token": "550e8400-e29b-41d4-a716-446655440000",
"room_number": "402", // 后端解析为 room_id
"guest_email": "new.guest@example.com" // 后端解析为 contact_id
}
代理永远不应为获取另一个操作的 ID 而调用 get_room_by_number 或 get_guest_by_email。每个工具参数都应使用业务标识符,ID 解析全部由后端内部完成。
2. 在工具设计中内置幂等性
所有创建或修改资源的工具都应接受 幂等性令牌:
{
"idempotency_token": "550e8400-e29b-41d4-a716-446655440000",
"guest_email": "beth.gibbs@email.com",
"description": "Toilet broken in room 302"
}
当代理重试(它一定会)时,后端识别重复请求并返回原始结果。幂等性是后端的责任,而非代理的责任。
额外收益: 对于跨多个系统的操作(例如跨支付处理和 CRM 更新的退房流程),幂等性令牌可以实现 saga‑pattern 编排。如果支付成功但 CRM 更新失败,后端可以使用该令牌协调补偿事务(如退款),而无需代理介入。
3. 原子化协调状态转换
当客人办理入住时,需要同时完成多件事:
- 预订状态:
Reserved → Checked In - 房间状态:
Available → Occupied - 商机阶段:
Pending → Active
这些不应是代理必须协调的三个独立工具。一个工具(check_in_guest)应原子化地编排整个状态转换。
4. 在工具设计中嵌入授权
不要暴露无限制的搜索工具:
[
"search_all_cases",
"search_all_rooms",
"search_all_bookings"
]
而是设计具备适当作用域的工具:
[
"search_cases_on_behalf_of_guest(guest_email)",
"search_rooms_on_behalf_of_guest(guest_email)",
"search_rooms_on_behalf_of_staff(floor_filter, status_filter)"
]
工具接口本身即编码了 谁 可以看到 什么。授权变为声明式而非命令式。
5. 提供智能默认值
尽可能降低代理的认知负荷:
{
"guest_email": "required",
"check_in_date": "defaults to today",
"number_of_guests": "defaults to 1",
"status_filter": "defaults to 'Open'"
}
代理只需指定真正可变的字段。
6. 记录前置条件和错误
(原文片段已截断 – 请务必为每个工具列出明确的必备前置条件,并提供简洁的错误处理指南。)
7. 支持带有明确语义的部分更新
更新操作应易于推理:
{
"external_id": "required",
"check_in_date": "optional – only changes if provided",
"room_number": "optional – only changes if provided",
"guest_email": "optional – only changes if provided"
}
“仅提供需要更改的字段——其余保持不变。”
这比强迫代理执行读‑改‑写要简单得多。
8. 创建防御性组合助手
So
Source: …
我的操作需要前置条件。与其强迫代理执行 检查‑然后‑创建:
# idempotent helper
create_contact_if_not_found(email, first_name, last_name)
此帮助函数可以安全地被编排工具调用,以确保前置条件存在。
9. 为自然语言模式设计
倾听人们实际的说法:
| 自然语言 | 工具名称 |
|---|---|
| “为 Beth Gibbs 办理入住” | check_in_guest |
| “302 房间的厕所坏了” | submit_maintenance_request |
| “把预订改到 402 房间” | manage_bookings |
工具名称和参数应与用户自然使用的语言保持一致。
TL;DR
- 围绕用户意图设计工具,而不是低层 API 调用。
- 使用业务标识符,让后端解析内部 ID。
- 使每个变更工具具备幂等性,并能够进行原子状态更改。
- 在工具签名中直接编码授权和合理的默认值。
遵循这些模式,你将把 MCP 从脆弱的“胶水层”转变为强大、可组合的技能集,使 LLM 代理能够像真正的领域专家一样行动。 🚀
r 模式
工具描述应引导代理成功
Check‑in 工具:
“验证客人/预订的前提条件,检查房间空余情况,执行状态转换。返回预订和房间详情或错误代码(404:未找到客人/预订,409:存在多个预订或房间不可用)。”
当代理事先了解失败模式时,它可以优雅地处理这些情况,或在尝试操作之前提出澄清性问题。
可组合工具背后的架构
这些九个模式源自一个统一的架构原则:让 LLM 处理意图,让后端处理执行。
- LLMs – 为理解人类交流而优化的概率系统。
- Backends – 为可靠的状态管理和事务一致性而优化的确定性系统。
当你模糊这条边界——让 LLM 编排多步操作,或让后端解析自然语言——就会得到既不可靠也不智能的系统。
模式映射
| 模式组 | 实现功能 |
|---|---|
| 1, 5, 9 – 业务标识符、智能默认值、自然语言对齐 | LLM 处理人类概念;系统层面的细节交给后端。 |
| 2, 3, 6 – 幂等性、原子转移、错误模式 | 后端保证可靠性;LLM 无需考虑重试或故障恢复。 |
| 4, 7, 8 – 授权范围、部分更新、防御性助手 | 工具接口编码业务规则;后端验证并强制执行约束。 |
架构收益
当后端负责编排(良好设计):
- 单一实现,经过测试和验证
- 事务一致性得到保证
- 可观察的状态转变
- 可在不同接口间复用(Web、移动、MCP)
当 LLM 负责编排(糟糕设计):
- 逻辑分散在对话中
- 非确定性协调
- 不透明的失败(难以调试)
- 上下文膨胀(例如,工具超过 50 个,每个任务调用超过 6 次)
实际影响
在构建 Dewy Resort 应用时,我们逐步用基于技能的架构设计替换了直接的 API 调用和 API 工具包装器。下面的基准测试展示了差异。
| 指标 | 组合设计前 | 组合设计后 |
|---|---|---|
| 平均响应时间 | 8–12 秒 | 2–4 秒 |
| 成功率 | 73 % | 94 % |
| 工具数量 | 47 | 12 |
| 每次交互的平均工具调用次数 | 6.2 | 1.8 |
| 用户反馈 | “它能工作,但速度慢,有时会混乱。” | “它就是能工作。” |
企业 MCP 工具设计实现清单
在为生产系统设计 MCP 工具时,请自问:
身份与解析
- 工具是否接受业务标识符(电子邮件、姓名、号码)?
- 后端是否处理 ID 解析?
安全与可靠性
- 创建工具是否需要幂等性令牌?
- 状态转换是否原子化?
- 是否在操作前验证前置条件?
授权与访问
- 工具是否在接口中编码授权范围?
- 搜索工具是否限定在适当的上下文中?
认知负荷
- 工具是否提供合理的默认值?
- 工具名称是否与自然语言保持一致?
- 描述是否记录错误模式?
灵活性
- 更新操作是否支持部分更新?
- 代理是否可以使用业务标识符修改关系?
更广泛的模式
这些模式适用于您在构建与企业系统和流程交互的 AI 代理的任何场景。
| Domain | Intent‑driven example |
|---|---|
| Healthcare | “为该患者安排后续随访” → 协调预约预订、通知、记录更新。 |
| Finance | “提交这份费用报告” → 处理验证、审批流转、会计分录。 |
| Retail | “处理此退货” → 协调库存、退款、客户通知。 |
问题始终相同: 您是围绕用户意图设计工具,还是围绕 API 操作设计工具?
结论
Enterprise MCP 为您提供工具互操作性的基础。可组合、基于技能的设计是您在此基础上构建有用东西的方式。
该协议无法拯救您免于糟糕的架构,但良好的架构——围绕用户意图组合的工具——可以。
以意图驱动的架构:从好奇到生产级
停止包装 API。开始组合技能。
你的用户会感谢你。你的代理会感谢你。(好吧,你的代理可能不会。)但你的运维团队一定会感谢你。
你在 MCP 工具设计方面有什么经验?
我很想了解你发现了哪些模式。留下评论或在 LinkedIn 上联系——我们分享的模式越多,大家构建更好 AI 系统的速度就越快。
本文基于 Beyond Basic MCP: Why Enterprise AI Needs Composable Architecture 撰写,文中我探讨了使 MCP 在生产环境中有用的架构原则。