MCP 协议深度解析系列写到第七篇了,前面把 Tools 和 Prompts 聊透之后,这次终于轮到 Resources 资源系统。我自己的感受是,Resources 在三层能力里关注度最低,但实际做 Agent 应用时它是绕不开的——你的大模型要回答问题,总得先拿到数据;数据从哪来、怎么定位、怎么感知变化,就是 Resources 这套机制要解决的事。这篇文章会把 URI、订阅、内容管理这三块从头到尾拆一遍,包括协议层的交互细节、我在服务端实现时踩过的坑,以及对接 Dify、Claude、Cursor 这类客户端时的常见问题。
如果你正在写 MCP server,或者想把自家的文档库、数据库、监控面板接到 Agent 里当上下文,这篇文章应该能让你少走不少弯路。就算你只是用现成 MCP 服务的用户,搞清楚 Resources 的工作原理,也能帮你更快定位“为什么 Agent 总说找不到数据”这类问题。
1. 先搞清楚:Resources 在 MCP 协议里的角色定位
1.1 为什么聊完 Tools 和 Prompts 之后,才轮到 Resources
MCP(Model Context Protocol)给服务端定义了三种能力:Tools、Resources、Prompts。从优先级上说,Tools 是大多数开发者最先接触的,因为你让 Agent 去“做事”时,调用的都是工具;Prompts 则是给模型提供 Prompt 模板化能力,把一些固定的指令编排存到服务端。Resources 排到第三种,不是因为不重要,而是它的作用更像“地基”——模型在执行任务前需要先了解背景信息,这些信息大多通过 Resources 暴露。
举个我实际遇到的场景。之前我做一个内部运维数据聚合的 MCP server,Tools 里注册了很多操作类接口,比如执行部署、拉取日志、查询服务器状态。但如果模型不知道当前有哪些服务器、每个服务器的业务归属是什么,它连“该调用哪个工具、传什么参数”都判断不了。这个“元信息”本来可以写死在 Prompt 里,可一旦服务器列表经常变动,写死就是自找麻烦。用 Resources 来暴露这些数据,Agent 就能在需要时动态读取,不用把数据塞进超大上下文里。
所以你可以把 Resources 理解为 MCP 里的“只读上下文数据源”。它不产生副作用,不执行业务操作,职责非常专一:向上层模型提供可读取的结构化或非结构化数据。
1.2 Resources、Tools、Prompts 三者如何分工
很多新手会把 Resources 和 Tools 搞混,觉得“读数据不也是一个操作吗,为什么不能做成 Tool”。这里面的分界线其实很清楚:
| 能力 | 是否允许副作用 | 典型用途 | 触发方式 |
|---|---|---|---|
| Tools | 通常有副作用 | 执行操作:下单、部署、发消息、改配置 | 由模型根据任务主动决定调用 |
| Resources | 严格只读 | 提供数据:查询结果、文档内容、配置信息 | 模型读取,或作为上下文注入 |
| Prompts | 无副作用 | 模板化指令:生成代码前的要求、报告写作框架 | 用户或模型按需加载 |
理论上,一个“读取服务器列表”的功能用 Tools 也能实现,模型调一个 list_servers 工具就拿到数据了。但这么做有几个问题:第一,Tools 的设计意图是动作,会让模型在“该读数据”和“该执行操作”之间失去判断力;第二,许多客户端会在系统启动时通过 resources/list 预加载资源清单,把资源写成 Tool 就破坏了这种机制;第三,涉及敏感数据的只读访问,用 Resources 更容易在权限模型上做区分。
简单说:要执行动作的走 Tools,要读取上下文的走 Resources,要预设行为模板的走 Prompts,三者各司其职。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. URI:MCP 资源系统的寻址基础
2.1 MCP 里的 URI 到底长什么样
Resources 之所以叫“资源系统”,核心就在“资源”的定位方式上。MCP 选用了 URI(统一资源标识符)作为定位标准,而不是自己发明一套新的寻址规则。这一点我觉得是协议设计上非常聪明的地方——URI 是互联网行业几十年的通用规范,客户端和服务端都能快速理解。
一个标准的 MCP URI 包含几个部分:
code复制scheme://authority/path?query#fragment
比如 file:///home/user/docs/report.pdf,scheme 是 file,authority 为空,path 是 /home/user/docs/report.pdf。再比如 postgres://database/orders/2025/01,scheme 是 postgres,后面跟着数据库路径和表名。MCP 规范并没有限制 URI 的 scheme 必须是 http、file 这些标准协议,服务端完全可以自定义 scheme,比如 knowledge://project/123/doc/456、sensor://factory/line-1/temperature。
我在实现时最常用的是自定义 scheme,因为标准 scheme 的表达能力有限。比如我想暴露“某个项目下的所有 Issues”,用 issue://repo/owner/issue/123 这种 URI 就比 https://... 简洁得多,模型读起来也更容易理解层级关系。
2.2 资源模板的设计与使用场景
如果你的资源是在服务启动前就能确定集合的,那直接用静态 URI 列表就行。但现实中更多情况是:资源存在,但数量很大,或
