资讯动态

MCP 模型上下文协议理论篇5:Resources 资源机制与 URI 设计实战

发布时间:2026/10/3 7:04:28 来源:尧图企业网站定制
1. 为什么你的 MCP Server 需要 Resources从日志读取场景说起如果你正在写 MCP Server大概率已经写过 Tools模型决定调用哪个函数、传什么参数服务器执行完把结果塞回上下文。但有一类需求 Tools 处理起来很别扭——数据本身就在那里不需要模型“决定调用”而是客户端应用希望把它作为上下文挂上去。比如本地日志文件、数据库里的一张配置表、当前屏幕截图、一份 PDF 报告。这些东西的共同点是它们是“被读取的对象”而不是“被执行的函数”。MCP 里的 Resources资源就是干这个的。它把服务器端的数据和内容暴露给客户端由应用程序控制何时读取、怎么使用。这一点和 Tools 的边界非常关键Tools 是模型控制的Resources 是应用控制的。你在实现资源支持时要准备好面对不同客户端的交互模式——有的客户端要求用户显式勾选资源后才注入上下文有的会按启发式规则自动挑选还有的会把选择权交给模型自己。所以官方文档里有一句很实在的话如果你想自动向模型暴露数据应该用 Tools而不是 Resources。这篇聚焦 Resources 的核心概念与 URI 设计模式面向正在构建 MCP Server 的开发者。我会给出可复制的 Resources 定义 JSON 配置、URI 模板示例以及在本地 MCP Client 中验证资源读取与订阅通知的完整操作步骤。读完你应该能分清 Resources、Tools、Prompts 三者的协作边界并且能自己跑通一次resources/list→resources/read→resources/subscribe的闭环。先明确 Resources 能承载什么文件内容、数据库记录、API 响应、实时系统数据、截图与图像、日志文件基本任何类型的数据都可以。每个资源由一个唯一 URI 标识内容可以是 UTF-8 文本也可以是 base64 编码的二进制。文本资源适合源代码、配置文件、日志、JSON/XML二进制资源适合图像、PDF、音频、视频。这个分类直接决定了你返回时用text字段还是blob字段后面配置章节会具体写。2. TaoToken 前置准备给 MCP Client 配一个稳定的模型入口在验证 Resources 之前你需要一个能跑起来的 MCP Client 环境。我用的是 Claude Code 这类支持 MCP 的客户端它需要连接一个模型服务来驱动对话。这里用 TaoToken 作为模型接入层它的 API 地址是https://taotoken.net/api兼容 Anthropic 与 OpenAI 风格的调用方式配置起来比较直接。先说清楚为什么这一步不能跳过MCP Server 本身只是暴露资源和工具真正发起resources/read请求、把内容拼进上下文的是客户端里的模型会话。如果你的模型入口没配好客户端连对话都跑不起来更别说验证资源订阅通知了。所以先把模型通道打通再回头调 Server。TaoToken 的接入文档在https://taotoken.net/docAPI Keys 管理页在https://taotoken.net/api-keys。你需要先去 API Keys 页面生成一个 Key格式通常是sk-开头的一串字符。拿到 Key 之后根据你用的客户端类型选择配置方式如果是 Claude Code走 Anthropic 兼容配置如果是 Cline、Continue 这类走 OpenAI 兼容配置。两种方式的 Base URL 都指向https://taotoken.net/api区别在路径后缀和请求头。这里有个容易踩的坑很多人把 Base URL 写成https://taotoken.net漏掉了/api结果请求打到官网首页返回 HTML客户端报Unexpected token in JSON。记住 API 入口是带/api的官网是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content两者不要混。如果你打算长期跑编码类 Agent 任务可以了解下 Coding Plan入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。它适合需要持续调用模型的场景比按次计费更省心。不过验证 Resources 阶段用普通 API Key 就够了不必一上来就上套餐。配置完成后先用模型对话页面做一次连通性测试地址是https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。发一句“你好”能正常返回就说明 Key 和 Base URL 没问题。这一步过了再进入下一章的 Server 配置。3. 可复制配置Resources 定义 JSON 与 URI 模板实战这一章是核心。我会给出一个完整的 MCP Server 资源配置包含resources/list的返回结构、URI 模板定义、以及resources/read的处理逻辑。你可以直接复制到自己的项目里改。先看资源发现的两种方式。直接资源通过resources/list暴露具体列表每个资源包含uri、name、description、mimeType四个字段。资源模板用于动态资源用 RFC 6570 的 URI 模板语法客户端拿到模板后自己填充参数构造出有效 URI。两者的 JSON 结构如下{ resources: [ { uri: file:///logs/app.log, name: Application Logs, description: 应用运行日志按天滚动, mimeType: text/plain }, { uri: postgres://database/customers/schema, name: Customers Schema, description: 客户表结构定义, mimeType: application/json } ], resourceTemplates: [ { uriTemplate: file:///logs/{date}.log, name: Daily Log, description: 按日期读取日志date 格式 YYYY-MM-DD, mimeType: text/plain }, { uriTemplate: screen://localhost/display{displayId}, name: Screen Capture, description: 截取指定显示器画面, mimeType: image/png } ] }URI 的格式是[协议]://[主机]/[路径]。协议和路径结构完全由你的 Server 定义可以自定义 scheme。上面例子里file://、postgres://、screen://都是合法的。设计 URI 时有几个原则值得遵守协议名要能表达数据来源类型路径要稳定可预测动态部分用模板参数而不是让客户端拼字符串。比如file:///logs/{date}.log就比让客户端自己拼file:///logs/2024-01-01.log更安全因为模板明确了参数格式。接下来是resources/read的处理。客户端发来请求带上 URI服务器返回contents数组。注意这个数组可以包含多个资源——比如读取一个目录时可以把目录下所有文件一次性返回。文本用text字段二进制用blob字段base64 编码{ contents: [ { uri: file:///logs/app.log, mimeType: text/plain, text: 2024-01-01 10:00:00 INFO server started\n... }, { uri: screen://localhost/display1, mimeType: image/png, blob: iVBORw0KGgoAAAANSUhEUg... } ] }如果你用 TypeScript 写 Server处理逻辑大概是这样server.setRequestHandler(ReadResourceRequestSchema, async (request) { const uri request.params.uri; if (uri file:///logs/app.log) { const logContents await readLogFile(); return { contents: [ { uri, mimeType: text/plain, text: logContents } ] }; } if (uri.startsWith(file:///logs/)) { const date uri.replace(file:///logs/, ).replace(.log, ); const dailyLog await readLogByDate(date); return { contents: [ { uri, mimeType: text/plain, text: dailyLog } ] }; } throw new Error(Resource not found); });声明能力时别忘了在 Server 初始化里加上capabilities: { resources: {} }否则客户端不会调用资源相关端点。如果你还要支持订阅需要额外声明resources: { subscribe: true }。订阅机制分两种。列表变化服务器主动发notifications/resources/list_changed告诉客户端可用资源列表变了。内容变化客户端先发resources/subscribe带上 URI服务器在资源更新时发notifications/resources/updated客户端收到通知后再发resources/read拿最新内容不需要了发resources/unsubscribe取消。这个设计的好处是客户端不用轮询服务器也不用推送完整内容只推一个“变了”的信号。4. 验证请求在本地 MCP Client 中跑通读取与订阅配置写完了得实际验证。我用 Claude Code 作为本地 MCP Client 来演示其他客户端操作类似。先确认你的 Server 能被客户端启动通常是在客户端的 MCP 配置里加一段 server 定义指定启动命令和参数。启动后第一步验证resources/list。在客户端里触发资源列表查询你应该能看到第 3 章配置的那些资源。如果列表为空检查 Server 是否声明了resources能力以及ListResourcesRequestSchema的 handler 是否注册成功。第二步验证resources/read。选中file:///logs/app.log这个资源客户端会发读取请求。成功的标志是你能在对话上下文里看到日志内容被注入。这里有个细节不同客户端对资源的处理方式不同。Claude Desktop 目前要求用户显式选择资源后才使用所以你得手动勾选有些客户端会自动按启发式规则挑选还有的会让模型自己决定用哪个。你实现 Server 时要能兼容这些模式别假设客户端一定会自动读取。第三步验证订阅通知。先发resources/subscribe订阅file:///logs/app.log然后在服务器端手动改一下这个文件的内容观察客户端是否收到notifications/resources/updated。收到后客户端应该自动或手动触发一次resources/read拿最新内容。如果没收到通知检查两点Server 能力里是否声明了subscribe: true以及订阅请求的 URI 是否和资源列表里的完全一致包括大小写和斜杠。验证过程中可以用模型对话页面辅助观察上下文变化地址是https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。把资源内容注入后问模型“日志里最后一条错误是什么”如果它能答出来说明资源确实进了上下文。实测下来最容易出问题的是 URI 匹配。比如你列表里写的是file:///logs/app.log读取时客户端传的是file:///logs/app.log看起来一样但如果中间有 URL 编码差异空格变成%20就会匹配失败。建议在 Server 里对 URI 做一次规范化处理再比较。5. 常见报错排查401、local proxy failed 与 reading choices这一章对照真实报错来排。Resources 本身是 MCP 协议层的东西但验证时你往往会先撞上模型接入层的错误所以两类都要覆盖。401 Unauthorized。这个通常出现在客户端连模型服务时不是 MCP Server 的问题。原因一般是 API Key 没配、配错或者 Base URL 写成了官网地址。检查你的配置文件里ANTHROPIC_API_KEY或OPENAI_API_KEY是否填了sk-开头的 KeyBase URL 是否是https://taotoken.net/api。如果 Key 是对的还报 401去 API Keys 页面确认这个 Key 没有被删除或过期入口https://taotoken.net/api-keys。local proxy failed。这个报错说明客户端尝试走本地代理但失败了。常见原因是环境变量里残留了HTTP_PROXY或HTTPS_PROXY指向一个不存在的本地端口。解决办法是清掉这些环境变量或者确认你的网络配置不需要代理。注意这里不涉及任何网络工具纯粹是环境变量清理。reading choices。这是 OpenAI 兼容接口的典型报错通常写成Cannot read properties of undefined (reading choices)。意思是客户端期望返回体里有choices字段但实际拿到的响应结构不对。原因可能是 Base URL 路径错了请求打到了非 API 端点返回了 HTML 或错误 JSON。确认 Base URL 带/api并且你用的模型 ID 在 TaoToken 支持的列表里。如果用的是 Claude Code 走 Anthropic 格式返回结构里是content而不是choices别把两种格式的客户端配置搞混。OAuth 相关报错。有些客户端在连接远程 MCP Server 时会走 OAuth 流程如果 Server 没实现授权端点就会报错。本地验证阶段建议先用 stdio 方式启动 Server避开 OAuth。等 Resources 逻辑验证通了再考虑远程部署和授权。资源读取返回空。如果resources/read返回了contents: []检查 handler 里的 URI 分支是否命中。建议在 handler 开头打一行日志把收到的 URI 原样输出对比资源列表里的 URI。另外注意contents数组里每个元素都必须有uri字段漏了会导致客户端解析失败。订阅后收不到通知。除了前面说的能力声明和 URI 匹配还要确认你的 Server 真的在资源变化时调用了发送通知的方法。很多框架需要你显式调用server.notification()之类的接口光改文件不会自动触发。排查时建议按顺序来先确认模型通道通能对话再确认 MCP Server 启动成功列表能返回最后确认资源读取和订阅。一层一层往下别跳步。6. 把 Resources 用对边界、协作与下一步Resources、Tools、Prompts 三者在 MCP 里各管一摊边界清楚了实现才不会拧巴。Resources 是应用控制的数据暴露适合“把这份日志/这张表/这个文件作为上下文”Tools 是模型控制的动作执行适合“帮我查一下、算一下、改一下”Prompts 是预定义的提示模板适合“按这个格式帮我生成”。三者可以协作比如一个 Tool 执行完产生了一份报告把报告注册成一个 Resource客户端再决定要不要把它注入后续对话。URI 设计上我的经验是尽量让 scheme 表达数据域路径表达层级动态部分用模板参数。不要设计过于复杂的嵌套 URI客户端解析起来容易出错。二进制资源记得正确设置mimeType否则客户端可能不知道怎么渲染。如果你还没配好模型入口先去https://taotoken.net/api-keys拿 Key接入文档在https://taotoken.net/doc。验证模型连通性用https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。长期跑编码 Agent 任务可以看 Coding Plan入口https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。下一步建议你把订阅通知接进一个真实场景比如监控一个配置文件变化时自动刷新上下文。跑通这个Resources 就算真正用起来了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑