MCP 的 Resources 与 Prompts 详解
作者:程序员马丁
Ragent AI —— 从 0 到 1 纯手工打造企业级 Agentic RAG,拒绝 Demo 玩具!AI 时代,助你拿个offer。
上一篇 MCP 文章重点讲了 Tools——用 @McpTool 注解定义工具,模型判断什么时候调、调哪个,你的代码负责执行。整个流程跑通之后,你可能觉得 MCP 的核心就是 Tools,Resources 和 Prompts 只是协议里的附属品。
当时那篇也确实写了一句:“本篇重点讲 Tools,Resources 和 Prompts 在实际项目中用得相对少一些,了解即可。”在很多简单项目里,Resources 和 Prompts 确实不会第一时间用到。但在多 Client 接入、需要可复用架构的场景下,它们的价值会变得很明显。
但实际项目做下来,你会发现,有些场景用 Tools 硬做反而别扭。
比如:你的 AI 应用需要在对话前加载当前系统的配置信息,或者读取某张数据库表的表结构。用 Tools 怎么做?写一个 getAppConfig 工具,模型调用它,返回配置内容。能跑通,但总觉得哪里不对——这个操作没有任何副作用,不会修改数据,不会触发流程,它只是读一份资料而已。用 Tools 来做,就像你去餐厅点了一道菜,结果服务员端上来的是一本菜单。
再比如:你们团队沉淀了一套效果很好的知识库问答 Prompt(角色定义、回答规则、引用要求、兜底策略)。这套 Prompt 不只是一段文本——它精确地规定了指令和问题怎么组装、消息的顺序和结构。你希望所有接入的 Client 都能直接用这套模板,用户在 Client 里选一下模板、传入参数,就能拿到一组结构化的 messages 数组。不用每个 Client 自己去理解这套 Prompt 的结构,也不用自己拼装消息。当然,Client 拿到这组 messages 后是原样发给模型还是再做加工,仍然由 Client 自己决定——Prompts 提供的是标准化的模板描述与参数化机制,不是自动发送机制。
这正是 Resources 和 Prompts 在协议层面被单独抽出来的原因。今天把这个坑补上。
Resources:让 Server 暴露数据给模型
1. Resources 是什么
Resources(资源)是 MCP 协议提供的三类能力之一。一句话概括:Server 暴露数据,Client 来读取,给模型提供上下文。
和 Tools 的区别很关键,用餐厅来打比方:
- Tools 像点菜——你告诉厨房要做一道红烧肉,厨房开火炒菜,这个过程有副作用(消耗食材、产生一道菜)。对应到系统里就是:下订单(写数据库)、发邮件(触发外部操作)、提交退货申请(修改订单状态)
- Resources 像看菜单——你拿起菜单翻了翻,看看有什么菜、价格多少。这个过程没有任何副作用,菜单不会因为你看了一眼就少一页。对应到系统里就是:获取应用配置、查看数据库表结构、读取系统状态
协议层面的定义也很直接:
| 维度 | Tools | Resources |
|---|---|---|
| 本质 | 执行操作 | 提供数据 |
| 副作用 | 可能有(可读可写) | 无(只读) |
| 谁来决定调用 | 模型决定(model-controlled) | 用户/应用决定(application-controlled) |
| 协议方法 | tools/call | resources/read |
| 类比 | 点菜 | 看菜单 |
注意谁来决定调用这一行。Tools 是模型驱动的——模型分析用户意图后自己决定要不要调工具。而 Resources 是应用驱动的——通常由 Host 应用或用户手动选择要读取哪些资源,作为上下文提供给模型。
打个比方:Tools 像是模型的手,它自己伸手去做事;Resources 像是你递给模型的参考资料,你决定给它看什么。
2. 实际场景理解 Resources
2.1 客服系统的上下文预加载
光看定义可能还是觉得抽象,用一个企业客服场景来感受一下 Resources 的价值。
用户打开在线客服对话窗口,还没开口说话,你的系统就已经知道了这个用户是谁。这时候,程序员在 Host 应用的代码里预设好逻辑:用户进入对话时,主动从 MCP Server 读取一批 Resources,作为上下文提供给模型:
customer://users/user_12345/profile→ 这个用户的会员等级、注册时间、历史投诉记录customer://users/user_12345/recent-orders→ 最近 3 笔订单信息docs://return-policy→ 当前退货政策docs://vip-privileges→ VIP 会员专属权益
Host 代码大致长这样:
// 程序员在 Host 应用里写的编排逻辑
public void onUserEnterChat(String userId) {
// 主动读取这个用户相关的 Resources
String profile = mcpClient.readResource("customer://users/" + userId + "/profile");
String orders = mcpClient.readResource("customer://users/" + userId + "/recent-orders");
String policy = mcpClient.readResource("docs://return-policy");
// 把这些内容拼到上下文里,发给模型
String context = profile + "\n" + orders + "\n" + policy;
callLLM(systemPrompt, context, userMessage);
}
用户开口说:“我买的东西有问题想退”,模型已经知道他是金卡会员、上周刚买了一台 iPhone、享受 15 天无理由退货——一轮对话就能给出精准回答。
注意:这里读取 Resources 后塞进上下文是 Host 应用的编排策略,不是 MCP Resources 协议自动完成的。MCP 协议定义了资源如何被列出(
resources/list)、如何被读取(resources/read),但读到之后怎么用、要不要放进模型上下文,由 Host 应用自己决定。
如果这些信息全用 Tools 来做会怎样?模型需要先判断“我该查什么”,然后依次调用工具:
用户:我买的东西有问题想退
模型:(分析后决定调用工具)→ tool_calls: getUserProfile, getRecentOrders, getReturnPolicy
Host:执行三个工具调用,返回结果
模型:根据查到的信息,您的 iPhone 可以在 15 天内退货...
能跑通,但有两个问题:一是额外的推理和调用延迟——模型要先推理出需要调哪些工具,每个工具调用都有网络开销;二是漏调用的风险——模型不一定每次都能意识到要查这三样东西,它可能只查了订单,忘了查会员等级,给出的答案就少了 VIP 专属权益这块。
Resources 的价值就在这里:应用提前把该给的上下文全给了,模型不用猜,没有额外的推理链路和工具调用延迟,也不会漏掉关键信息。
2.2 Java 微服务项目里 Resources 可能有点鸡肋
你可能会想:那我不用 MCP Resources,直接在 Host 代码里查数据库拼上下文,效果不是一样吗?
如果你是 Java 程序员,项 目本身就是微服务架构——用户服务、订单服务、内容服务各自独立部署,通过 Feign / Dubbo / gRPC 互相调用。那上面这个场景,你完全可以这么写:
public void onUserEnterChat(String userId) {
// 直接通过微服务远程调用获取数据
UserProfile profile = userServiceClient.getProfile(userId);
List<Order> orders = orderServiceClient.getRecentOrders(userId);
String policy = contentServiceClient.getReturnPolicy();
String context = buildContext(profile, orders, policy);
callLLM(systemPrompt, context, userMessage);
}
效果一模一样,而且你对 Feign / Dubbo 这套东西已经很熟了,调用链路清晰,类型安全,还有现成的熔断、重试、监控。绕一圈走 MCP Resources 协议,反而多了一层抽象,没有明显收益。
所以结论是:如果你的 AI 应用只有一个 Client(你自己的后端服务),而且已经有成熟的微服务体系,Resources 确实不是必选项,直接用现有的远程调用就好。
MCP Resources 真正有优势的场景是跨 Client 共享数据源——同一个 MCP Server 暴露的资源,Claude Desktop 能读、Cursor 能读、你自己的 Web 应用也能读,大家通过统一的 resources/read 协议获取数据,数据获取逻辑写在 Server 端一次,不用每个 Client 各写一套。如果你的系统只有一个 Client,这个优势就不存在了。
3. 两种资源类型
MCP 协议定义了两种资源类型:直接资源(Direct Resources)和资源模板(Resource Templates)。
3.1 直接资源(Direct Resources)
直接资源有一个固定的 URI,指向一个确定的数据。就像一个固定的文件路径,任何时候访问都是同一份数据(内容可能更新,但地址不变)。
docs://product-manual → 产品手册
config://app/settings → 应用配置
file:///var/log/app.log → 应用日志
直接资源适合那些数量有限、相对固定的数据——你的系统里有哪些资源是明确的,可以在 Server 启动时就注册好。