5.1 资源与资源模板:URI 即接口


文档摘要

5.1 资源与资源模板:URI 即接口 本节摘要:第 4 章讲了模型驱动的工具,本节讲应用驱动的资源。资源是 MCP 三大原语里的「只读数据提供者」,类比 Web API 的 GET——它不改世界,只把数据喂给模型上下文。本节的核心是讲清资源的两种形态:具体资源(URI 无参数,固定数据)与资源模板(URI 带 ,参数化数据)。资源的设计哲学是「URI 即接口」——URI 既是地址,也是参数来源。读完本节,你能用 注册两类资源,并说清它们的差别。 一、资源是什么:应用驱动的只读数据 回顾第 2.2 节的原语划分:资源是应用(宿主)决定加载的原语,类比 GET,只读。

5.1 资源与资源模板:URI 即接口

本节摘要:第 4 章讲了模型驱动的工具,本节讲应用驱动的资源。资源是 MCP 三大原语里的「只读数据提供者」,类比 Web API 的 GET——它不改世界,只把数据喂给模型上下文。本节的核心是讲清资源的两种形态:具体资源(URI 无参数,固定数据)与资源模板(URI 带 {param},参数化数据)。资源的设计哲学是「URI 即接口」——URI 既是地址,也是参数来源。读完本节,你能用 @mcp.resource(uri) 注册两类资源,并说清它们的差别。

一、资源是什么:应用驱动的只读数据

回顾第 2.2 节的原语划分:资源是应用(宿主)决定加载的原语,类比 GET,只读。

宿主准备调用模型前 │ ▼ 「这次对话需要项目配置上下文」 宿主决定加载 config://app 资源 │ ▼ 客户端发 resources/read 请求 服务端返回配置内容 │ ▼ 宿主把内容塞进模型上下文 模型带着这份上下文推理

资源的关键特征:

  • 只读:不改变世界,只是提供数据
  • 应用驱动:由宿主(不是模型)决定何时加载
  • 预加载时序:在模型说话前就进上下文,扩充「背景知识」

这几点与工具(模型驱动、有副作用、运行时调用)形成鲜明对比。

二、URI 即接口

资源的设计哲学浓缩为一句话:URI 即接口。URI 既是资源的「地址」(客户端据此找到它),也是参数来源(URI 里的占位符就是参数):

URI: config://app │ │ │ └── 资源标识(名为 app 的配置) └── 方案(自定义,体现来源) URI: db://users/{id} │ │ │ │ │ └── 参数占位符(填用户 ID) │ └── 资源类型(用户) └── 方案(数据库)

把 URI 想象成 Web API 的路径,config://app 类比 GET /config/app,db://users/{id} 类比 GET /db/users/{id}。这个类比贯穿整个资源体系。

三、具体资源:URI 无参数

最简单的资源形态是具体资源——URI 里没有占位符,代表一份固定数据:

@mcp.resource("config://app") def get_config() -> dict: """Return app configuration.""" return {"theme": "dark", "lang": "zh", "timeout": 30}

具体资源的特点:

  • 可直接列举:它会出现在 resources/list 的结果里
  • URI 即唯一标识:一个 URI 对应一份固定数据
  • 适合:只有一份固定数据(配置、状态、说明文档)
客户端调 resources/list │ ▼ 服务端返回:[ {uri: "config://app", name: "get_config", description: "..."}, {uri: "status://health", ...}, ... ] ← 具体资源都在这里

四、资源模板:URI 带

另一种形态是资源模板——URI 里有 {param} 占位符,代表一类参数化数据:

@mcp.resource("db://users/{user_id}") def get_user(user_id: str) -> dict: """Get a user by ID.""" return fetch_user_from_db(user_id)

资源模板的特点:

  • 不能直接列举:它不在 resources/list,而在 resources/templates/list(单独的模板列表)
  • 需实例化才能读:必须提供参数填进 URI,才能读具体数据
  • 适合:同一模式、参数化数据(按 ID 查用户、按日期查日志)
客户端调 resources/templates/list │ ▼ 服务端返回:[ {uriTemplate: "db://users/{user_id}", ...}, {uriTemplate: "log://{date}", ...} ] 客户端要读时: resources/read db://users/u123 ← 填参数 user_id=u123 │ ▼ 服务端解析 URI,提取 user_id="u123" 调用 get_user("u123") 返回用户数据

五、两种形态的对照

把两种形态放一起对照,差异更清晰:

维度 具体资源 资源模板
URI 形式 无参数(config://app) {param}(db://users/{id})
能否直接列举 能,在 resources/list 不能,在 resources/templates/list
读取方式 直接 resources/read <uri> 先填参数实例化 URI,再 read
数据特征 一份固定数据 一类参数化数据
类比 Web GET /config(单数) GET /users/{id}(参数化路由)

💡 技巧:判断该用哪种,问一句:「这个资源的数据是一份,还是一类?」 一份固定数据→具体资源;一类按参数变化的数据→资源模板。例:应用配置是一份(具体资源),用户数据是一类(资源模板)。

六、URI 占位符与函数参数的对应

资源模板有个细节值得强调——URI 占位符的名字,必须与函数参数名一致:

@mcp.resource("db://users/{user_id}") # URI 占位符叫 user_id def get_user(user_id: str) -> dict: # 函数参数也叫 user_id ...

SDK 用这个对应关系,把 URI 里解析出的值,映射成函数调用:

读取请求:db://users/u123 │ ▼ SDK 解析 URI 匹配模板:db://users/{user_id} 提取参数:user_id = "u123" │ ▼ 调用函数 get_user(user_id="u123")

如果 URI 占位符叫 {user_id} 但函数参数叫 id,SDK 会对不上,导致调用失败。所以起名要一致——这是资源模板的约定。

七、资源的返回内容

资源返回的内容,会被宿主塞进模型上下文。返回类型灵活:

@mcp.resource("config://app") def get_config() -> dict: # 字典 → JSON 文本 return {"theme": "dark"} @mcp.resource("readme://project") def get_readme() -> str: # 字符串 → 原样 return "# 项目说明\n..." @mcp.resource("image://{name}") def get_image(name: str) -> bytes: # 字节 → 二进制内容 return load_image(name)

不同返回类型适合不同场景:文本/JSON 给模型读、字节给富内容(图片等)。SDK 会按类型包装成对应的内容格式发回客户端。

本节要点回顾

  1. 资源是应用驱动的只读数据,类比 GET,在模型说话前预加载进上下文。
  2. URI 即接口:URI 既是地址,也是参数来源(占位符即参数)。
  3. 具体资源:URI 无参数,固定数据,可直接列举,适合一份固定数据。
  4. 资源模板:URI 带 {param},参数化数据,需实例化才能读,适合一类数据。
  5. 判断该用哪种:问「数据是一份还是一类」,一份→具体,一类→模板。
  6. URI 占位符名必须与函数参数名一致,SDK 据此映射 URI 到函数调用。
  7. 返回类型灵活:字典/字符串/字节,SDK 按类型包装成内容格式。

资源清楚了,下一节回答一个关键追问——「为什么不直接用工具」。


发布者: 作者: 灏天文库 转发
评论区 (0)
U