MCP能力发现
3999 字约 13 分钟
AIAgentMCP能力发现
2026-07-24
MCP 能力发现是协议生命周期的核心阶段——Client 与 Server 在建立连接后,通过标准化方法协商、声明和获取彼此支持的功能集合。没有能力发现,Agent 无法知道可以做什么、该怎么做。
一、基本定义
能力发现(Capability Discovery) 是 MCP 协议中 Client 了解 Server 提供哪些功能的过程。
在 MCP 中,Server 可以暴露三类能力:
- Tools:可执行的操作(函数)
- Resources:可读取的数据源
- Prompts:预定义的提示模板
能力发现不是单一动作,而是一个分阶段流程:先通过初始化握手交换顶层能力声明,再按需获取具体能力列表。
二、为什么需要能力发现
在 Agent 与多个外部系统交互时,能力发现解决三个核心问题:
- Agent 不知道有什么:模型无法凭空猜测 Server 提供了哪些工具或资源,必须有一个显式的发现机制
- Server 能力是动态的:同一个 Server 在不同运行环境中可能提供不同功能,静态配置无法覆盖
- 多 Server 协同:当 Host 连接多个 Server 时,Agent 需要知道每个 Server 的能力边界,才能正确路由任务
没有能力发现,MCP 就退化为一堆硬编码的 API 集成——这正是 MCP 要解决的问题。
三、在 MCP 体系中的位置
能力发现位于 MCP协议生命周期 的初始化阶段之后、能力使用阶段之前:
连接建立 → 初始化(capabilities 交换)→ 能力发现(tools/list 等)→ 能力使用(tools/call 等)它既是协议生命周期的一个阶段,也是运行时持续发生的过程——Server 能力可以在运行中变化。
四、Server 能力声明
4.1 初始化时的 capabilities 交换
MCP 连接建立后,Client 和 Server 各自发送 initialize 请求,其中包含 capabilities 字段:
{
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": {},
"resources": { "subscribe": true },
"prompts": {}
},
"serverInfo": {
"name": "example-server",
"version": "1.0.0"
}
}Client 也会发送自己的能力:
{
"capabilities": {
"roots": { "listChanged": true }
},
"clientInfo": {
"name": "example-client",
"version": "1.0.0"
}
}这个阶段的 capabilities 对象是顶层声明,告诉对方"我支持哪些功能类别"。
4.2 Server capabilities 对象
Server 的 capabilities 对象中,每个键对应一个功能类别:
| 字段 | 含义 |
|---|---|
tools | 声明支持 Tool 调用 |
resources | 声明支持 Resource 访问 |
resources.subscribe | 是否支持资源订阅 |
resources.listChanged | 是否支持资源列表变更通知 |
prompts | 声明支持 Prompt 模板 |
prompts.listChanged | 是否支持 Prompt 列表变更通知 |
tools.listChanged | 是否支持工具列表变更通知 |
logging | 是否支持日志功能 |
注意:如果某个功能类别在 capabilities 中缺失,Client 不应假设该功能可用,也不应调用对应的 list 方法。
五、Tool 列表发现
5.1 tools/list 请求
初始化完成后,Client 通过 tools/list 方法获取 Server 暴露的所有工具:
{
"method": "tools/list"
}Server 返回工具列表:
{
"tools": [
{
"name": "search_notes",
"description": "在知识库中搜索笔记",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"limit": {
"type": "integer",
"description": "返回结果数量上限",
"default": 10
}
},
"required": ["query"]
}
}
]
}5.2 Tool 的结构
每个 Tool 包含三个核心字段:
- name:工具的唯一标识符,用于后续调用
- description:工具的功能描述,是模型理解工具用途的主要依据
- inputSchema:JSON Schema 格式的参数定义,描述调用时需要的输入
5.3 tools/listChanged 通知
如果 Server 在 capabilities 中声明了 tools.listChanged,当工具列表发生变化时,Server 会发送通知:
{
"method": "notifications/tools/list_changed"
}Client 收到通知后应重新调用 tools/list 获取最新列表。
六、Resource 列表发现
6.1 resources/list 请求
{
"method": "resources/list"
}返回示例:
{
"resources": [
{
"uri": "file:///notes/daily/2026-07-23.md",
"name": "今日笔记",
"description": "2026年7月23日的日记笔记",
"mimeType": "text/markdown"
}
]
}6.2 Resource 的结构
每个 Resource 包含:
- uri:资源的唯一标识符(URI 格式)
- name:人类可读的名称
- description(可选):资源描述
- mimeType(可选):资源的 MIME 类型
6.3 resources/listChanged 通知
与 Tool 类似,当 Server 声明了 resources.listChanged 能力后,资源列表变化时发送:
{
"method": "notifications/resources/list_changed"
}6.4 Resource Templates
Server 还可以提供资源模板,用于生成动态 URI:
{
"method": "resources/templates/list"
}返回示例:
{
"resourceTemplates": [
{
"uriTemplate": "file:///notes/{date}.md",
"name": "日期笔记",
"description": "按日期访问笔记",
"mimeType": "text/markdown"
}
]
}资源模板让 Client 知道可以通过参数化的 URI 模式访问动态资源。
七、Prompt 列表发现
7.1 prompts/list 请求
{
"method": "prompts/list"
}返回示例:
{
"prompts": [
{
"name": "code_review",
"description": "对代码进行审查并提供改进建议",
"arguments": [
{
"name": "language",
"description": "编程语言",
"required": true
},
{
"name": "style",
"description": "审查风格:严格/宽松",
"required": false
}
]
}
]
}7.2 Prompt 的结构
每个 Prompt 包含:
- name:Prompt 的唯一标识符
- description(可选):Prompt 的用途描述
- arguments:参数列表,每个参数包含 name、description、required
7.3 prompts/listChanged 通知
{
"method": "notifications/prompts/list_changed"
}八、能力缓存
8.1 为什么需要缓存
每次交互都重新获取完整能力列表是不经济的。对于拥有上百个 Tool 的 Server,频繁请求会:
- 增加协议开销
- 延迟 Agent 的响应速度
- 对 Server 造成不必要的负载
Client 通常在初始化后缓存能力列表,后续直接使用缓存。
8.2 缓存失效策略
MCP 采用事件驱动的缓存失效机制:
- listChanged 通知:Server 主动通知 Client 列表已变化
- 重连时重新获取:连接断开重连后,Client 应清空缓存并重新获取
- 协议版本变更:协议版本变化时,能力可能不同,需重新发现
8.3 listChanged 通知触发重新获取
Client 处理流程:
收到 notifications/tools/list_changed
→ 标记 tools 缓存为失效
→ 调用 tools/list 获取新列表
→ 更新缓存
→ 更新 Agent 的可用工具上下文关键注意点:listChanged 只是一个通知,不携带具体变化内容。Client 必须重新获取完整列表,不能做增量更新。
九、动态更新
9.1 Server 运行时新增/移除能力
Server 可以在运行过程中动态变化其能力集合。常见场景:
- 后端服务上线后新增工具
- 配置变更导致某些工具不可用
- 插件热加载/卸载
9.2 通知机制
Server 通过 listChanged 通知告知 Client:
- 新增工具 → 发送
notifications/tools/list_changed - 移除工具 → 发送
notifications/tools/list_changed - 资源更新 → 发送
notifications/resources/list_changed
9.3 Client 如何响应变化
一个健壮的 Client 应该:
- 监听所有
listChanged通知 - 收到通知后重新获取对应类型的完整列表
- 对比新旧列表,更新 Agent 的上下文
- 如果正在使用的 Tool 被移除,需要通知 Agent 该工具不再可用
- 避免高频重复请求——如果短时间内收到多次通知,可以做防抖处理
十、分页
10.1 大量 Tool 时的分页处理
当 Server 提供大量能力时,返回结果可能很大。MCP 支持 cursor-based pagination:
请求时可以带上 cursor:
{
"method": "tools/list",
"params": {
"cursor": "eyJwYWdlIjogMn0="
}
}10.2 cursor-based pagination
响应中包含 nextCursor:
{
"tools": [ ... ],
"nextCursor": "eyJwYWdlIjogM30="
}- 如果响应包含
nextCursor,说明还有更多结果 - Client 应继续请求直到响应中不再包含
nextCursor cursor的具体值由 Server 定义,Client 只负责透传
分页对能力缓存同样重要——Client 必须获取所有分页才能构建完整的能力缓存。
十一、权限过滤
11.1 不同用户看到不同能力
在实际部署中,同一个 Server 可能对不同 Client 暴露不同的能力集合。例如:
- 管理员用户可以看到所有管理工具
- 普通用户只能看到基础操作工具
- 只读用户看不到写入类工具
11.2 基于权限的能力裁剪
Server 在实现 tools/list 时,根据请求方的身份或权限决定返回哪些工具。这是 Server 端的逻辑,协议本身不强制权限模型。
Client 侧需要注意:
- 不要假设缓存中的能力列表对所有用户都相同
- 权限变化时 Server 应发送
listChanged通知 - 调用
tools/call时 Server 仍需做权限校验——能力发现阶段的过滤是优化,不是安全边界
十二、能力冲突
12.1 多个 Server 提供同名 Tool
当 Host 连接多个 Server 时,可能出现 Tool 名称冲突:
- Server A 提供
search(搜索笔记) - Server B 提供
search(搜索数据库)
12.2 命名空间策略
MCP 协议本身不强制命名空间,但实践中常见的解决方式:
- Client 前缀:Host/Client 在合并工具列表时自动添加 Server 标识前缀
- Server 自行前缀:Server 在定义 Tool 时使用有区分度的名称
- 用户手动配置:用户通过配置指定优先级或排除规则
12.3 Client 如何选择
当发生冲突时,Client 的策略包括:
- 拒绝加载冲突的工具,要求用户解决
- 自动添加前缀区分
- 按 Server 优先级选择
- 将所有工具暴露给 Agent,让模型根据描述自行选择
十三、能力命名规范
好的 Tool 命名应该:
- 自解释:名称本身就表达功能意图(
search_notes优于do_search) - 一致风格:同一 Server 内的命名保持一致(全用 snake_case 或 camelCase)
- 避免歧义:不使用过于通用的名称(
process、handle) - 动词+名词:遵循
action_target模式(create_note、list_files、read_config)
Resource URI 命名建议:
- 使用有意义的 URI scheme
- 层级结构清晰(
file:///project/src/main.py) - 避免包含敏感信息
十四、描述质量
14.1 好的工具描述如何帮助模型选择
Tool 的 description 是模型决策的关键输入。模型通过描述来判断:
- 这个工具是否能完成当前任务
- 何时应该调用这个工具而非其他工具
- 参数应该如何填写
14.2 描述对 Agent 决策的影响
好的描述特征:
- 明确做什么:"搜索 Obsidian 知识库中的笔记"
- 明确不做什么:如果容易与其他工具混淆,说明边界
- 参数说明:在 inputSchema 中为每个参数提供清晰的 description
- 使用场景:说明什么情况下应该使用这个工具
差的描述特征:
- 过于模糊:"处理数据"
- 技术内部术语:"调用 foo_bar API"
- 缺少参数说明,让模型猜测参数含义
十五、能力注册中心与 Marketplace 的安全风险
当能力通过注册中心或 Marketplace 分发时,能力发现引入新的攻击面:
- 恶意 Server:提供看似正常但实际执行有害操作的工具
- 描述欺骗:工具名称和描述看起来很无害,但实际行为不同
- 供应链攻击:通过自动发现机制注入恶意能力
- 能力膨胀:大量无关工具降低 Agent 决策质量
缓解措施:
- Client 应维护可信 Server 列表
- 能力发现应有人工审批环节
- 对工具描述做安全扫描
- 限制自动发现的工具数量上限
十六、能力发现流程
十七、设计原则
- 渐进发现:先交换顶层能力,再按需获取详细列表,避免一次性传输过多信息
- 事件驱动更新:通过
listChanged通知实现按需刷新,而非轮询 - Server 诚实声明:Server 必须在
capabilities中如实声明支持的功能,不要声明了但不实现 - Client 防御性编程:Client 不应信任 Server 返回的数据,需要校验 Schema 合法性
- 描述面向模型:Tool 描述的主要消费者是 LLM,不是人类开发者
- 分页是必须的:当能力数量可能较大时,必须实现分页
十八、常见误区
误区:能力发现是一次性操作
- 事实:能力发现贯穿整个连接生命周期,Server 可以随时变化
误区:listChanged 包含变化详情
- 事实:
listChanged只是信号,不携带具体内容,Client 必须重新获取完整列表
- 事实:
误区:能力发现阶段过滤了权限就够了
- 事实:发现阶段的过滤是用户体验优化,
tools/call时仍需服务端权限校验
- 事实:发现阶段的过滤是用户体验优化,
误区:Server 声明了 capabilities 就一定能用
- 事实:声明只是说"我支持这个类别",具体 list 可能返回空数组
误区:所有工具都应该暴露给模型
- 事实:过多工具会降低模型决策质量,应做合理筛选和分组
误区:cursor 有统一格式
- 事实:cursor 是 Server 定义的不透明字符串,Client 只做透传
十九、实践检查清单
Server 端:
Client 端:
二十、与其他概念的关系
- MCP基础:能力发现是 MCP 协议的核心机制之一
- MCP架构总览:能力发现在 Host → Client → Server 架构中定义了信息流向
- MCP协议生命周期:能力发现是初始化后的第一个关键阶段
- MCP Tool能力:
tools/list是能力发现中最重要的环节 - MCP资源模型:
resources/list和resources/templates/list构成资源发现 - MCP Prompt能力:
prompts/list发现可用的提示模板 - MCP Server设计:Server 设计时需要规划能力声明和发现策略
- MCP Client设计:Client 设计需要实现完整的能力发现和缓存机制
- MCP安全边界:能力发现阶段是安全过滤的第一道关卡
- MCP与Agent协作:Agent 依赖能力发现结果来规划任务执行
二十一、适用边界
能力发现机制适用于:
- MCP 协议定义的标准 Client-Server 通信场景
- 运行时动态获取外部系统能力的需求
- 多 Server 环境下的能力聚合和路由
不适用于:
- 非 MCP 协议的 API 发现(如 OpenAPI Spec、GraphQL Introspection)
- 能力的运行时性能特征发现(MCP 不暴露延迟、吞吐量等信息)
- 跨 Server 的能力依赖关系声明
- 能力的版本兼容性协商(协议版本是全局的,不是按能力协商的)