1. 资源是什么? #

资源(Resources) 是 MCP 服务器暴露的只读数据,供 AI 或用户当作上下文参考。

对比 资源(Resources) 工具(Tools)
作用 读数据 做事情
谁决定用 用户 / 应用 大模型
类比 打开文档看一眼 执行一条命令
会改数据吗 不会(只读) 可能会

典型例子:应用配置、用户偏好、某篇笔记、文件内容、数据库查询结果。

你需要知道的三个协议方法:

方法 作用 客户端 SDK 写法
resources/list 列出静态资源 await session.list_resources()
resources/templates/list 列出资源模板 await session.list_resource_templates()
resources/read 读取指定 URI 的内容 await session.read_resource(uri)

2. 两种资源类型 #

每个资源用 URI 唯一标识(像网址,但不必真是 http)。

类型 URI 特征 函数参数 示例
静态资源 地址固定,无 {} 无参数 config://app
模板资源 含 {参数} 占位符 参数名与占位符一致 note://{title} → note://周报

生活类比:

3. 读取资源的流程 #

sequenceDiagram participant App as 客户端/应用 participant S as MCP服务器 App->>S: resources/list S-->>App: [config://app, ...] App->>S: resources/templates/list S-->>App: [note://{title}, ...] App->>S: resources/read(uri) S-->>App: 资源文本内容 Note over App: 把内容放进 AI 上下文

与工具不同:资源不会由模型自动「调用」,通常由应用或用户选择后,再 read_resource 交给大模型。

4. 资源在协议里长什么样? #

4.1 静态资源列表(list 响应片段) #

{
  "resources": [
    {
      "uri": "config://app",
      "name": "config://app",
      "mimeType": "application/json"
    }
  ]
}

4.2 资源模板列表 #

{
  "resourceTemplates": [
    {
      "uriTemplate": "note://{title}",
      "name": "note://{title}",
      "mimeType": "text/plain"
    }
  ]
}

4.3 读取资源(read 响应片段) #

{
  "contents": [
    {
      "uri": "config://app",
      "mimeType": "application/json",
      "text": "{\"theme\": \"dark\", \"lang\": \"zh-CN\"}"
    }
  ]
}

5. 服务器端:注册资源 #

用 `@mcp.resource("uri")装饰函数即可,FastMCP 自动处理resources/list和resources/read`。

5.1 静态资源 #

# 导入 json 模块,用于操作 JSON 数据
import json
# 从 mcp.server.fastmcp 中导入 FastMCP 类
from mcp.server.fastmcp import FastMCP

# 实例化 FastMCP 对象,名称为 "resource-demo"
mcp = FastMCP(name="resource-demo")

# 使用 @mcp.resource 装饰器注册资源,指定 URI 和 mime_type
@mcp.resource("config://app", mime_type="application/json")
# 定义 app_config 函数,返回类型为 str
def app_config() -> str:
    # 为该函数添加文档字符串说明,表示为应用配置(静态资源,无参数)
    """应用配置(静态资源,无参数)。"""
    # 将字典转为 JSON 字符串,不转义中文,返回结果
    return json.dumps({"theme": "dark", "lang": "zh-CN"}, ensure_ascii=False)

# 使用 @mcp.resource 装饰器注册模板资源,URI 中 {title} 占位符会传递给函数参数 title,指定返回的内容类型为 text/plain
@mcp.resource("note://{title}", mime_type="text/plain")
# 定义 read_note 函数,接收一个字符串类型的 title 参数,返回一个字符串
def read_note(title: str) -> str:
    # 添加函数文档字符串,说明这是“按标题读取笔记” 
    """按标题读取笔记。"""
    # 返回包含标题和示例内容的字符串,格式化插入 title
    return f"笔记标题:{title}\n内容:这是 MCP 资源示例。"

# 判断当前模块是否为主程序入口
if __name__ == "__main__":
    # 启动 MCP 服务,采用 stdio 作为传输方式
    mcp.run(transport="stdio")

客户端读取时传完整 URI:note://weekly-report → 服务器调用 read_note(title="weekly-report")。

5.2 常用 URI 方案 #

方案 示例 说明
自定义 config://app 最常见,自己定义 scheme
file:// file:///D:/docs/readme.md 文件系统服务器常用
https:// 远程 URL 客户端能直接拉取时用

6. 客户端:列出并读取资源 #

初始化完成后,依次 list_resources → list_resource_templates → read_resource。

6.1 示例 #

resources_client.py

# 导入 asyncio 模块,用于异步编程
import asyncio
# 导入 os 模块,用于处理文件和目录
import os
# 导入 sys 模块,获取解释器信息
import sys

# 从 pydantic 导入 AnyUrl,用于 URL 类型的数据验证
from pydantic import AnyUrl
# 从 mcp 包导入 ClientSession、StdioServerParameters 和 types
from mcp import ClientSession, StdioServerParameters, types
# 从 mcp.client.stdio 导入 stdio_client,用于标准输入输出通信
from mcp.client.stdio import stdio_client

# 定义一个异步主函数
async def main() -> None:
    # 获取当前文件的绝对路径并获取其所在目录
    base_dir = os.path.dirname(os.path.abspath(__file__))
    # 拼接出服务器脚本的路径
    server_path = os.path.join(base_dir, "resources_server.py")

    # 创建 StdioServerParameters 实例,指定命令和参数
    server_params = StdioServerParameters(
        command=sys.executable,
        args=[server_path],
    )

    # 使用 stdio_client 启动服务器,获取读写对象
    async with stdio_client(server_params) as (read, write):
        # 创建客户端会话,传入读写对象
        async with ClientSession(read, write) as session:
            # 初始化会话
            await session.initialize()

            # 列出静态资源
            resources = await session.list_resources()
            # 打印所有静态资源的 URI
            print("静态资源:", [str(r.uri) for r in resources.resources])

            # 列出资源模板
            templates = await session.list_resource_templates()
            # 打印所有资源模板的 URI 模板
            print("资源模板:", [t.uriTemplate for t in templates.resourceTemplates])

            # 读取静态资源,URI 为 config://app
            result = await session.read_resource(AnyUrl("config://app"))
            # 遍历返回的内容块
            for block in result.contents:
                # 如果内容块是文本资源内容类型
                if isinstance(block, types.TextResourceContents):
                    # 打印文本内容
                    print("[config://app]", block.text)

            # 读取模板资源,URI 填入参数值(建议英文路径,中文易被编码)
            result = await session.read_resource(AnyUrl("note://weekly-report"))
            # 遍历返回的内容块
            for block in result.contents:
                # 如果内容块是文本资源内容类型
                if isinstance(block, types.TextResourceContents):
                    # 打印文本内容
                    print("[note://weekly-report]", block.text)

# 判断是否为主模块运行
if __name__ == "__main__":
    # 运行主异步函数
    asyncio.run(main())

6.2 运行 #

uv run resources_client.py  

期望输出:

静态资源: ['config://app']
资源模板: ['note://{title}']
[config://app] {"theme": "dark", "lang": "zh-CN"}
[note://weekly-report] 笔记标题:weekly-report
内容:这是 MCP 资源示例。

6.3 API 说明 #

方法 返回 用途
list_resources() .resources 列表 发现所有静态资源 URI
list_resource_templates() .resourceTemplates 列表 发现带占位符的模板
read_resource(AnyUrl(...)) .contents 列表 获取实际文本/二进制内容

6.4 读取返回内容 #

read_resource 的 contents 里每个块可能是:

类型 字段 说明
TextResourceContents .text 文本内容(最常用)
BlobResourceContents .blob base64 编码的二进制

判断方式:

# 导入 mcp 模块中的 types 类型定义
from mcp import types

# 遍历 result.contents 中的每一个内容块
for block in result.contents:
    # 判断当前块是否为 TextResourceContents 类型(即文本资源内容)
    if isinstance(block, types.TextResourceContents):
        # 打印出文本内容
        print(block.text)

7. 用 Inspector 测试资源 #

npx @modelcontextprotocol/inspector uv run  resources_server.py
步骤 操作
1 确认 Connected
2 打开 Resources 标签
3 在列表里点 config://app 预览内容
4 对模板资源,在 URI 栏输入 note://测试 后读取