上一篇文章介绍了 MCP 的基本原理。
MCP 真正有价值的地方,并不是「把一个 API 包装成 Tool」这么简单。对于 AI Agent 来说,一个 Tool 本质上是一份让模型理解外部能力的接口契约。
传统 API 主要服务于程序员和程序。开发者知道接口文档在哪里、参数是什么意思、什么情况下应该调用哪个接口。
Agent 不一样。
Agent 必须根据当前任务、上下文以及 Tool 的描述,自主判断:
这个 Tool 能不能解决当前问题?
应该什么时候调用?
参数应该怎么填写?
哪些参数必须提供?
调用结果意味着什么?
如果调用失败,应该怎么修正?
因此,一个功能正确的 Tool,并不一定是一个好 Tool。
Anthropic 在 2025 年发布的 Tool Engineering 实践中也明确强调:Agent Tool 应该按照 Agent 的使用方式进行设计,而不是简单地把传统软件 API 原样暴露给模型。
这也是 MCP Tool Design 最值得关注的地方。
1. Tool 不是普通函数,而是给 Agent 使用的接口
假设我们有一个获取用户信息的 API:
GET /api/users/{id}
传统程序调用它非常简单:
$user = $client->getUser(10086);
程序员知道:
10086
是用户 ID。
但是 Agent 看到的可能只是一个 Tool:
{
"name": "get_user",
"description": "Get user information",
"inputSchema": {
"type": "object",
"properties": {
"user": {
"type": "string"
}
}
}
}
这时候问题就出现了。
user 到底是:
用户 ID?
用户名?
邮箱?
手机号?
模型需要自己猜。
更合理的设计应该是:
{
"name": "get_user",
"description": "Get a user's profile by their unique user ID. Use this tool when you need the profile information of a specific user.",
"inputSchema": {
"type": "object",
"properties": {
"user_id": {
"type": "integer",
"description": "The unique numeric ID of the user."
}
},
"required": ["user_id"]
}
}
两者的后端实现可能完全一样,但对于 Agent 来说,它们是两个完全不同质量的 Tool。
因此可以把 Tool 理解成:
Tool
├── Name
├── Description
├── Input Schema
├── Output
├── Error Semantics
└── Permission Boundary
这些信息共同构成了 Agent 使用 Tool 的「操作说明书」。
MCP 的 Tool 定义本身就包含名称、描述和输入 Schema 等元数据,供 AI 应用发现和调用工具。MCP Tools 规范
2. Tool Name:名称首先要让模型知道“它是什么”
Tool Name 看起来只是一个字符串,但它实际上是 Agent 判断工具用途的重要信号。
例如:
tool1
query
execute
handle
data
request
这些名字对于程序员来说可能还能理解,但对于 Agent 来说语义非常弱。
相比之下:
search_users
get_user
create_order
cancel_order
refund_order
search_documents
get_document
明显更容易理解。
一个好的 Tool Name 通常应该满足:
表达明确的业务动作;
避免无意义的通用名称;
与参数和返回结果保持一致;
与其他 Tool 形成清晰的语义边界。
例如:
search_users
get_user
create_user
update_user
delete_user
比:
user_action
user_operation
user_api
更容易让 Agent 建立正确的工具选择逻辑。
MCP 当前规范对 Tool Name 也有明确约束,工具名称应保持简洁、稳定,并避免空格等不适合作为机器标识符的字符。
Namespace 比单纯的命名更重要
当 Agent 同时连接多个 MCP Server 时,可能出现:
search
search
search
它们分别来自:
GitHub
Jira
Slack
这时 Tool 的语义边界就容易变得模糊。
可以通过命名空间建立边界:
github_search_issues
jira_search_issues
slack_search_messages
或者:
github_search
jira_search
slack_search
具体采用哪种形式,需要结合实际模型和评测结果确定。Anthropic 的实践也指出,Tool Namespace 可以帮助 Agent 在大量工具中建立更清晰的功能边界。
3. Description:Tool 最容易被低估的部分
很多开发者设计 MCP Tool 时,会把 Description 写成:
Search users.
或者:
Create an order.
从程序员角度看没有问题。
但是 Agent 需要的不只是「这个函数叫什么」。
它更需要知道:
这个 Tool 解决什么问题?
什么情况下应该使用?
什么情况下不应该使用?
参数代表什么?
返回什么?
有没有特殊限制?
例如:
{
"name": "search_orders",
"description": "Search orders by customer, order status, or creation time. Use this tool when the user asks to find, inspect, or filter existing orders. Do not use it to create or modify orders."
}
这就比:
{
"name": "search_orders",
"description": "Search orders."
}
更加明确。
Description 应该回答一个核心问题
可以把 Tool Description 写作:
「如果我是一个刚加入团队的开发者,我需要知道什么,才能正确使用这个工具?」
例如:
Search orders by customer ID, order status, or creation time.
Use this tool when you need to locate existing orders or inspect
their current status.
This tool does not create, modify, cancel, or refund orders.
If the user provides an order ID, prefer get_order instead.
这实际上已经不是简单的「API 文档」,而是在给 Agent 建立工具选择策略。
Anthropic 的 Tool Engineering 实践特别强调了这一点:Tool Description 和 Schema 会进入 Agent 的上下文,因此描述本身就是影响 Tool Calling 行为的重要 Context。
4. 参数设计:让模型“没有猜测空间”
Tool 的参数设计是另一个非常关键的地方。
例如:
{
"properties": {
"user": {
"type": "string"
}
}
}
这是一个典型的「看起来有 Schema,实际上没有提供足够语义」的设计。
应该尽可能明确:
{
"properties": {
"user_id": {
"type": "integer",
"description": "The unique numeric ID of the user."
}
},
"required": ["user_id"]
}
参数名称应该表达语义
不要:
user
id
type
value
data
query
除非上下文非常明确。
优先:
user_id
order_id
document_id
status
search_query
start_time
end_time
例如:
{
"properties": {
"query": {
"type": "string"
}
}
}
不如:
{
"properties": {
"search_query": {
"type": "string",
"description": "Keywords used to search document titles and content."
}
}
}
参数名称本身就是上下文。
Enum 能明确约束就不要让模型自由发挥
例如订单状态:
{
"status": {
"type": "string",
"enum": [
"pending",
"paid",
"shipped",
"completed",
"cancelled"
]
}
}
比:
{
"status": {
"type": "string"
}
}
更加可靠。
前者告诉模型:
只能从这几个值中选择。
后者实际上是在说:
你自己猜一个。
Required 也非常重要
例如:
{
"type": "object",
"properties": {
"user_id": {
"type": "integer"
},
"include_orders": {
"type": "boolean"
}
},
"required": ["user_id"]
}
这比所有参数都做成 optional 更好。
因为:
user_id
是执行操作必需的数据,而:
include_orders
只是可选行为。
Schema 应该尽可能把这种业务规则表达出来。
5. Tool 粒度:太小和太大都不好
Tool 设计中一个非常容易出现的问题,是到底应该拆多少个 Tool。
例如一个订单系统可能有:
get_order
get_order_items
get_customer
get_payment
get_shipping
get_order_logs
然后 Agent 为了回答:
「帮我看看订单 10086 为什么还没有发货。」
可能需要连续调用:
get_order
→ get_order_items
→ get_payment
→ get_shipping
→ get_order_logs
这会产生大量 Tool Call 和上下文。
另一种设计是:
get_order_context
直接返回:
{
"order": {},
"customer": {},
"payment": {},
"shipping": {},
"recent_events": []
}
Agent 一次调用就获得解决问题所需要的大部分上下文。
这并不意味着 Tool 越大越好。
例如:
execute_everything
把查询、创建、修改、删除、退款全部塞进去,同样是糟糕的设计。
更合理的原则是:
一个 Tool 应该围绕一个清晰的 Agent 任务能力设计,而不是机械地对应一个 API。
Anthropic 的实践也建议优先设计少量、高价值、目的明确的 Tool,而不是简单地把所有 API Endpoint 一一包装成 Tool。
可以简单理解成:
传统 API
API Endpoint → Tool
不一定是最佳方案。
更合理的是:
用户任务
↓
Agent 能力
↓
Tool
↓
多个 API / 数据源 / 内部操作
Tool 可以在内部完成多个低级操作,把中间过程隐藏起来。
6. Tool 返回结果:不要把数据库原样倒给 Agent
Tool 的输入需要设计,输出同样需要设计。
例如:
{
"id": 10086,
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"created_at": 1757923200,
"mime_type": "application/json",
"internal_status": 3,
"tenant_id": 18273,
"name": "MySQL Performance"
}
程序当然可以处理。
但 Agent 真正关心的可能只是:
{
"name": "MySQL Performance",
"status": "published",
"created_at": "2026-09-15",
"type": "document"
}
Tool 返回的数据应该围绕 Agent 下一步任务进行设计。
Anthropic 的实践中也特别强调了「high-signal context」:不要为了所谓的通用性,把大量低价值字段全部返回给模型。
返回结果要考虑 Token 成本
假设:
search_documents
一次返回:
1000 条记录 × 每条 500 Token
就是:
500,000 Token
这不仅浪费上下文,还可能导致 Agent 后续行为变差。
更合理的是:
{
"items": [
{
"id": 101,
"title": "MySQL Index Optimization",
"summary": "..."
}
],
"total": 1832,
"page": 1,
"page_size": 10,
"has_more": true
}
然后让 Agent:
搜索
↓
查看前 10 条
↓
需要更多结果
↓
继续分页
而不是:
一次返回全部数据
因此:
Pagination、Filtering、Truncation、Range Selection 都不仅仅是 API 性能设计,也是 Agent Context Engineering 的一部分。Anthropic 也建议对可能消耗大量上下文的 Tool 设计合理的分页、过滤和截断策略。
7. 错误处理:告诉 Agent“哪里错了,以及怎么改”
传统 API 经常返回:
{
"code": 40001,
"message": "Invalid parameter"
}
对于 Agent 来说,这种错误信息往往不够。
例如:
Invalid parameter
Agent 不知道:
哪个参数错了?
为什么错?
应该怎么改?
更好的 Tool Error 应该尽可能提供可恢复的信息:
{
"error": "invalid_parameter",
"parameter": "start_time",
"message": "start_time must be earlier than end_time",
"suggestion": "Set start_time to a timestamp before end_time."
}
Agent 就可以:
调用失败
↓
理解错误
↓
修改参数
↓
重新调用
而不是:
调用失败
↓
不知道为什么
↓
继续猜
这也是 Agent Tool 与传统 API 的一个重要区别:
错误信息本身也是给模型提供的 Context。
如果错误信息足够明确,Agent 可以自动恢复;如果错误信息只是一个错误码,Agent 很可能只能停止或者重新猜测。
8. 安全与幂等:Tool 不应该只有“能不能调用”
Tool 一旦拥有真实系统操作能力,安全边界就必须成为设计的一部分。
尤其是:
create
update
delete
cancel
refund
send
publish
execute
这些操作都有实际副作用。
例如:
refund_order
不能只定义:
{
"order_id": {
"type": "integer"
}
}
还需要明确:
- 是否允许退款?
- 最大退款金额是多少?
- 是否需要人工确认?
- 是否可以重复执行?
- 重复执行会发生什么?
幂等性
例如:
create_order
Agent 因为网络问题没有收到响应,可能再次调用。
如果接口不是幂等的:
第一次:创建订单
第二次:再次创建订单
就可能产生两个订单。
因此具有副作用的 Tool 应该考虑:
idempotency_key
或者由 Tool 内部保证幂等。
权限边界
不要因为 Agent 能调用:
delete_user
就意味着它应该拥有:
delete_any_user
可以进一步限制:
当前用户只能删除自己的资源
或者:
普通 Agent:
read-only
Admin Agent:
read/write
Privileged Agent:
destructive operations
MCP 的 Tool 规范也提供了 Tool Annotations 等机制,用于向客户端披露工具是否具有只读、破坏性、幂等等特征;但这些元数据不能替代真正的服务器端权限控制。MCP Tools 规范
最终的权限判断必须发生在服务端。
9. 一个完整 Tool 应该长什么样?
以知识库项目为例。
假设我们有一个:
search_documents
Tool。
一个比较合理的设计可以是:
{
"name": "search_documents",
"description": "Search documents in the knowledge base by semantic relevance and optional filters. Use this tool when you need to find documents or passages relevant to a user's question. Prefer specific queries over broad queries.",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The natural-language question or keywords to search for."
},
"category": {
"type": "string",
"description": "Optional document category used to narrow the search."
},
"top_k": {
"type": "integer",
"description": "Maximum number of relevant results to return.",
"minimum": 1,
"maximum": 20,
"default": 5
}
},
"required": ["query"]
}
}
返回:
{
"items": [
{
"document_id": 1024,
"title": "MySQL Index Optimization",
"content": "......",
"score": 0.91
},
{
"document_id": 1025,
"title": "InnoDB Index Structure",
"content": "......",
"score": 0.87
}
],
"total": 2,
"has_more": false
}
这个 Tool 已经明确表达了:
Name
↓
search_documents
Purpose
↓
搜索知识库
Input
↓
query
category
top_k
Constraints
↓
top_k 1~20
Output
↓
document_id
title
content
score
Agent 不需要猜。
这就是一个「Agent-friendly Tool」。
10. Tool Design 最终应该形成一套工程标准
如果把前面的内容总结成一套工程检查表,可以得到:
| 维度 | 推荐做法 |
|---|---|
| Name | 表达明确动作和资源 |
| Description | 说明用途、适用场景和边界 |
| Parameter | 使用语义明确的名称 |
| Type | 尽可能使用严格类型 |
| Enum | 对有限值进行约束 |
| Required | 明确真正必需的参数 |
| Tool 粒度 | 围绕 Agent 任务设计 |
| Output | 返回高价值信息 |
| Pagination | 避免大量数据进入 Context |
| Error | 返回可理解、可恢复的信息 |
| Idempotency | 副作用操作考虑幂等 |
| Permission | 服务端执行真正权限校验 |
| Security | 对破坏性操作建立边界 |
| Evaluation | 用真实任务验证 Tool 效果 |
其中最容易被忽略的是最后一项。
不要只测试“Tool 能不能运行”
传统 API 测试通常是:
输入
↓
调用 API
↓
检查返回值
Agent Tool 还需要测试:
用户任务
↓
Agent 是否选择正确 Tool
↓
是否填写正确参数
↓
Tool 是否成功执行
↓
Agent 是否正确理解结果
↓
是否能够继续完成任务
例如:
用户:
“帮我找一下最近关于 MySQL 深分页优化的资料,
然后总结一下有哪些优化方案。”
真正应该测试的是:
Agent
↓
search_documents
↓
是否正确生成 query?
↓
是否选择合理 top_k?
↓
是否需要再次搜索?
↓
是否正确理解搜索结果?
↓
是否完成总结?
Anthropic 的实践已经采用了这种 Evaluation-driven Tool Design:通过真实任务、可验证结果以及 Tool Call、错误率、Token 消耗等指标持续优化 Tool,而不是只验证函数本身是否正常工作。
这意味着:
一个 Tool 是否优秀,最终应该由 Agent 完成任务的效果来衡量,而不是由 Tool 本身的代码质量来衡量。
结语:MCP Tool Design,本质上是在设计 Agent 的“能力边界”
MCP 解决的是:
Agent
↓
如何标准化连接外部能力
而 Tool Design 解决的是:
Agent
↓
如何理解这些能力
↓
如何选择能力
↓
如何正确使用能力
↓
如何从结果中继续行动
所以,MCP Tool 并不是简单的:
API → MCP Tool
更准确的模型应该是:
业务能力
↓
Agent Task
↓
Tool Design
├── Name
├── Description
├── Input Schema
├── Output
├── Error
├── Permission
└── Safety
↓
MCP Tool
↓
AI Agent
真正优秀的 Tool 应该让 Agent 少猜、少调用、少犯错,并且能够从失败中恢复。
这也是 MCP 从「协议」走向「Agent 工程实践」之后,最值得关注的一层。