一、资源与模板概述
资源代表MCP客户端可以读取的数据或文件,而资源模板通过允许客户端基于URI中传递的参数请求动态生成的资源来扩展这一概念。
FastMCP简化了静态和动态资源的定义,主要使用@mcp.resource装饰器。
二、什么是资源?
资源为LLM或客户端应用程序提供对数据的只读访问。当客户端请求资源URI时:
- FastMCP找到相应的资源定义。
- 如果是动态的(由函数定义),则执行该函数。
- 内容(文本、JSON、二进制数据)返回给客户端。
这使得LLM能够访问文件、数据库内容、配置或与会话相关的动态生成信息。
三、资源
3.1 @resource装饰器
定义资源最常见的方法是通过装饰Python函数。装饰器需要资源的唯一URI。
python
import json
from fastmcp import FastMCP
mcp = FastMCP(name="DataServer")
# 返回字符串的基本动态资源
@mcp.resource("resource://greeting")
def get_greeting() -> str:
"""提供简单的问候消息。"""
return "Hello from FastMCP Resources!"
# 返回JSON数据的资源(字典自动序列化)
@mcp.resource("data://config")
def get_config() -> dict:
"""以JSON格式提供应用程序配置。"""
return {
"theme": "dark",
"version": "1.2.0",
"features": ["tools", "resources"],
}
关键概念:
- URI:@resource的第一个参数是唯一的URI(例如"resource://greeting"),客户端使用它来请求此数据。
- 懒加载:装饰的函数(get_greeting、get_config)仅在客户端通过resources/read特定请求该资源URI时执行。
- 推断的元数据:默认情况下:
- 资源名称:取自函数名称(get_greeting)。
- 资源描述:取自函数的文档字符串。
3.2 装饰器参数
您可以使用 @mcp.resource 装饰器中的参数自定义资源的属性:
python
from fastmcp import FastMCP
mcp = FastMCP(name="DataServer")
# 指定元数据的示例
@mcp.resource(
uri="data://app-status", # 显式URI(必需)
name="ApplicationStatus", # 自定义名称
description="提供应用程序的当前状态。", # 自定义描述
mime_type="application/json", # 显式MIME类型
tags={"monitoring", "status"}, # 分类标签
meta={"version": "2.1", "team": "infrastructure"} # 自定义元数据
)
def get_application_status() -> dict:
"""内部函数描述(如果上面提供了描述,则忽略)。"""
return {"status": "ok", "uptime": 12345, "version": mcp.settings.version} # 示例用法
@resource装饰器参数:

Annotations属性:

四、返回值
FastMCP自动将函数的返回值转换为适当的MCP资源内容:
- str:作为TextResourceContents发送(默认mime_type="text/plain")。
- dict、list、pydantic.BaseModel:自动序列化为JSON字符串并作为TextResourceContents发送(默认mime_type="application/json")。
- bytes:Base64编码并作为BlobResourceContents发送。您应指定适当的mime_type(例如"image/png"、"application/octet-stream")。
- None:导致返回空资源内容列表。
五、禁用资源
新版本2.8.0功能
您可以通过启用或禁用资源来控制资源和模板的可见性和可用性。禁用的资源不会出现在可用资源或模板列表中,尝试读取禁用的资源将导致"未知资源"错误。
默认情况下,所有资源都是启用的。您可以在创建时使用装饰器中的enabled参数禁用资源:
python
@mcp.resource("data://secret", enabled=False)
def get_secret_data():
"""此资源当前已禁用。"""
return "Secret data"
您也可以在创建后以编程方式切换资源的状态:
python
@mcp.resource("data://config")
def get_config(): return {"version": 1}
# 禁用和重新启用资源
get_config.disable()
get_config.enable()
六、访问MCP上下文
新版本2.2.5功能
资源和资源模板可以通过Context对象访问额外的MCP信息和功能。要访问它,请向资源函数添加一个类型注解为Context的参数:
python
from fastmcp import FastMCP, Context
mcp = FastMCP(name="DataServer")
@mcp.resource("resource://system-status")
async def get_system_status(ctx: Context) -> dict:
"""提供系统状态信息。"""
return {
"status": "operational",
"request_id": ctx.request_id
}
@mcp.resource("resource://{name}/details")
async def get_details(name: str, ctx: Context) -> dict:
"""获取特定名称的详细信息。"""
return {
"name": name,
"accessed_at": ctx.request_id
}
有关Context对象及其所有功能的完整文档,请参阅上下文文档。
七、异步资源
对于执行I/O操作(例如从数据库或网络读取)的资源函数,使用async def以避免阻塞服务器。
python
import aiofiles
from fastmcp import FastMCP
mcp = FastMCP(name="DataServer")
@mcp.resource("file:///app/data/important_log.txt", mime_type="text/plain")
async def read_important_log() -> str:
"""异步从特定日志文件读取内容。"""
try:
async with aiofiles.open("/app/data/important_log.txt", mode="r") as f:
content = await f.read()
return content
except FileNotFoundError:
return "Log file not found."
八、资源类
虽然 @mcp.resource 适用于动态内容,但您可以直接使用 mcp.add_resource() 和具体的 Resource 子类注册预定义的资源(如静态文件或简单文本)。
python
from pathlib import Path
from fastmcp import FastMCP
from fastmcp.resources import FileResource, TextResource, DirectoryResource
mcp = FastMCP(name="DataServer")
# 1. 直接公开静态文件
readme_path = Path("./README.md").resolve()
if readme_path.exists():
# 使用file:// URI方案
readme_resource = FileResource(
uri=f"file://{readme_path.as_posix()}",
path=readme_path, # 实际文件的路径
name="README File",
description="项目的README。",
mime_type="text/markdown",
tags={"documentation"}
)
mcp.add_resource(readme_resource)
# 2. 公开简单的预定义文本
notice_resource = TextResource(
uri="resource://notice",
name="Important Notice",
text="系统维护计划于周日进行。",
tags={"notification"}
)
mcp.add_resource(notice_resource)
# 3. 使用与URI不同的自定义键
special_resource = TextResource(
uri="resource://common-notice",
name="Special Notice",
text="这是一个具有自定义存储键的特殊通知。",
)
mcp.add_resource(special_resource, key="resource://custom-key")
# 4. 公开目录列表
data_dir_path = Path("./app_data").resolve()
if data_dir_path.is_dir():
data_listing_resource = DirectoryResource(
uri="resource://data-files",
path=data_dir_path, # 目录的路径
name="Data Directory Listing",
description="列出数据目录中可用的文件。",
recursive=False # 设置为True以列出子目录
)
mcp.add_resource(data_listing_resource) # 返回文件的JSON列表
常见资源类:
- TextResource:用于简单字符串内容。
- BinaryResource:用于原始bytes内容。
- FileResource:从本地文件路径读取内容。处理文本/二进制模式和懒读取。
- HttpResource:从HTTP(S) URL获取内容(需要httpx)。
- DirectoryResource:列出本地目录中的文件(返回JSON)。
- (FunctionResource:@mcp.resource使用的内部类)。
当内容是静态的或直接来自文件/URL时使用这些,绕过对专用Python函数的需求。
九、自定义资源键
新版本2.2.0功能
当直接使用mcp.add_resource()添加资源时,您可以选择性地提供自定义存储键:
python
# 创建具有标准URI作为键的资源
resource = TextResource(uri="resource://data")
mcp.add_resource(resource) # 将使用"resource://data"存储和访问
# 创建具有自定义键的资源
special_resource = TextResource(uri="resource://special-data")
mcp.add_resource(special_resource, key="internal://data-v2") # 将使用"internal://data-v2"存储和访问
请注意,此参数仅在直接使用add_resource()时可用,而不通过@resource装饰器可用,因为在使用装饰器时URI是显式提供的。
十、通知
新版本2.9.1功能
当资源或模板被添加、启用或禁用时,FastMCP会自动向连接的客户端发送notifications/resources/list_changed通知。这允许客户端保持与当前资源集的最新状态,而无需手动轮询更改。
python
@mcp.resource("data://example")
def example_resource() -> str:
return "Hello!"
# 这些操作触发通知:
mcp.add_resource(example_resource) # 发送resources/list_changed通知
example_resource.disable() # 发送resources/list_changed通知
example_resource.enable() # 发送resources/list_changed通知
仅当这些操作发生在活动的MCP请求上下文内(例如,从工具或其他MCP操作内调用时)才会发送通知。在服务器初始化期间执行的操作不会触发通知。
客户端可以使用消息处理程序来处理这些通知,以自动刷新其资源列表或更新其界面。
十一、注解
新版本2.11.0功能
FastMCP允许您通过注解向资源添加专门的元数据。这些注解向客户端应用程序传达资源的行为方式,而不会消耗LLM提示中的令牌上下文。
注解在客户端应用程序中有几个用途:
- 指示资源是只读的还是可能有副作用
- 描述资源的安全配置文件(幂等 vs. 非幂等)
- 帮助客户端优化缓存和访问模式
您可以使用@mcp.resource装饰器中的annotations参数向资源添加注解:
python
@mcp.resource(
"data://config",
annotations={
"readOnlyHint": True,
"idempotentHint": True
}
)
def get_config() -> dict:
"""获取应用程序配置。"""
return {"version": "1.0", "debug": False}
FastMCP支持这些标准注解:

请记住,注解有助于提供更好的用户体验,但应被视为建议性提示。它们帮助客户端应用程序呈现适当的UI元素和优化访问模式,但不会自行强制执行行为。始终专注于使您的注解准确反映资源的实际功能。
十二、资源模板
资源模板允许客户端请求其内容依赖于URI中嵌入的参数的资源。使用相同的@mcp.resource装饰器定义模板,但在URI字符串中包含{parameter_name}占位符,并向函数签名添加相应的参数。
资源模板与常规资源共享大多数配置选项(名称、描述、mime_type、标签、注解),但增加了定义URI参数的能力,这些参数映射到函数参数。
资源模板为每组唯一参数生成一个新资源,这意味着资源可以按需动态创建。例如,如果注册了资源模板"user://profile/{name}",MCP客户端可以请求"user://profile/ford"或"user://profile/marvin"来检索这两个用户配置文件中的任何一个作为资源,而无需单独注册每个资源。
提示:不支持将带有*args的函数作为资源模板。但是,与工具和提示不同,资源模板确实支持**kwargs,因为URI模板定义了将收集并作为关键字参数传递的特定参数名称。
这是一个完整的示例,展示如何定义两个资源模板:
python
from fastmcp import FastMCP
mcp = FastMCP(name="DataServer")
# 模板URI包括{city}占位符
@mcp.resource("weather://{city}/current")
def get_weather(city: str) -> dict:
"""提供特定城市的天气信息。"""
# 在实际实现中,这将调用天气API
# 这里我们为了示例目的使用简化逻辑
return {
"city": city.capitalize(),
"temperature": 22,
"condition": "Sunny",
"unit": "celsius"
}
# 具有多个参数和注解的模板
@mcp.resource(
"repos://{owner}/{repo}/info",
annotations={
"readOnlyHint": True,
"idempotentHint": True
}
)
def get_repo_info(owner: str, repo: str) -> dict:
"""检索GitHub存储库的信息。"""
# 在实际实现中,这将调用GitHub API
return {
"owner": owner,
"name": repo,
"full_name": f"{owner}/{repo}",
"stars": 120,
"forks": 48
}
定义了这两个模板后,客户端可以请求各种资源:
- weather://london/current → 返回伦敦的天气
- weather://paris/current → 返回巴黎的天气
- repos://jlowin/fastmcp/info → 返回jlowin/fastmcp存储库的信息
- repos://prefecthq/prefect/info → 返回prefecthq/prefect存储库的信息
十三、RFC 6570 URI模板
FastMCP实现了RFC 6570 URI模板用于资源模板,提供了一种标准化的方式来定义参数化URI。这包括对简单扩展、通配符路径参数和表单样式查询参数的支持。
13.1 通配符参数
新版本2.2.4功能
资源模板支持可以匹配多个路径段的通配符参数。虽然标准参数({param})只匹配单个路径段并且不跨越"/"边界,但通配符参数({param*})可以捕获多个段,包括斜杠。通配符捕获所有后续路径段,直到URI模板的定义部分(无论是字面量还是另一个参数)。这允许您在单个URI模板中拥有多个通配符参数。
python
from fastmcp import FastMCP
mcp = FastMCP(name="DataServer")
# 标准参数只匹配一个段
@mcp.resource("files://{filename}")
def get_file(filename: str) -> str:
"""按名称检索文件。"""
# 只匹配files://<single-segment>
return f"File content for: {filename}"
# 通配符参数可以匹配多个段
@mcp.resource("path://{filepath*}")
def get_path_content(filepath: str) -> str:
"""检索特定路径的内容。"""
# 可以匹配path://docs/server/resources.mdx
return f"Content at path: {filepath}"
# 混合标准参数和通配符参数
@mcp.resource("repo://{owner}/{path*}/template.py")
def get_template_file(owner: str, path: str) -> dict:
"""从特定存储库和路径检索文件,但
仅当资源以`template.py`结尾时"""
# 可以匹配repo://jlowin/fastmcp/src/resources/template.py
return {
"owner": owner,
"path": path + "/template.py",
"content": f"File at {path}/template.py in {owner}'s repository"
}
通配符参数在以下情况下很有用:
- 处理文件路径或分层数据
- 创建需要捕获可变长度路径段的API
- 构建类似于REST API的URL模式
请注意,与常规参数一样,每个通配符参数仍然必须是函数签名中的命名参数,并且所有必需的函数参数必须出现在URI模板中。
13.2 查询参数
新版本2.13.0功能
FastMCP支持使用{?param1,param2}语法的RFC 6570表单样式查询参数。查询参数提供了一种干净的方式来向资源传递可选配置,而不会使路径混乱。
查询参数必须是可选的函数参数(具有默认值),而路径参数映射到必需的函数参数。这强制执行了清晰的分离:必需的数据放在路径中,可选配置放在查询参数中。
python
from fastmcp import FastMCP
mcp = FastMCP(name="DataServer")
# 基本查询参数
@mcp.resource("data://{id}{?format}")
def get_data(id: str, format: str = "json") -> str:
"""以指定格式检索数据。"""
if format == "xml":
return f"<data id='{id}' />"
return f'{{"id": "{id}"}}'
# 具有类型强制的多个查询参数
@mcp.resource("api://{endpoint}{?version,limit,offset}")
def call_api(endpoint: str, version: int = 1, limit: int = 10, offset: int = 0) -> dict:
"""使用分页调用API端点。"""
return {
"endpoint": endpoint,
"version": version,
"limit": limit,
"offset": offset,
"results": fetch_results(endpoint, version, limit, offset)
}
# 具有通配符的查询参数
@mcp.resource("files://{path*}{?encoding,lines}")
def read_file(path: str, encoding: str = "utf-8", lines: int = 100) -> str:
"""使用可选编码和行限制读取文件。"""
return read_file_content(path, encoding, lines)
示例请求:
- data://123 → 使用默认格式"json"
- data://123?format=xml → 使用格式"xml"
- api://users?version=2&limit=50 → version=2, limit=50, offset=0
- files://src/main.py?encoding=ascii&lines=50 → 自定义编码和行限制
FastMCP根据函数的类型提示(int、float、bool、str)自动将查询参数字符串值强制转换为正确的类型。
查询参数 vs. 隐藏默认值:
查询参数向客户端公开可选配置。要完全向客户端隐藏可选参数(始终使用默认值),只需从URI模板中省略它们:
python
# 客户端可以通过查询字符串覆盖max_results
@mcp.resource("search://{query}{?max_results}")
def search_configurable(query: str, max_results: int = 10) -> dict:
return {"query": query, "limit": max_results}
# 客户端无法覆盖max_results(不在URI模板中)
@mcp.resource("search://{query}")
def search_fixed(query: str, max_results: int = 10) -> dict:
return {"query": query, "limit": max_results}
13.3 模板参数规则
新版本2.2.0功能
FastMCP在创建资源模板时强制执行这些验证规则:
- 必需的函数参数(无默认值)必须出现在URI路径模板中
- 查询参数(使用{?param}语法指定)必须是具有默认值的可选函数参数
- 所有URI模板参数(路径和查询)必须作为函数参数存在
可选的函数参数(具有默认值的参数)可以是:
- 包含为查询参数({?param})- 客户端可以通过查询字符串覆盖
- 从URI模板中省略 - 始终使用默认值,不向客户端公开
- 用于替代路径模板 - 启用访问同一资源的多种方式
一个函数的多个模板:
通过手动应用装饰器创建多个资源模板,通过不同的URI模式公开同一函数:
python
from fastmcp import FastMCP
mcp = FastMCP(name="DataServer")
# 定义一个用户查找函数,可以通过不同的标识符访问
def lookup_user(name: str | None = None, email: str | None = None) -> dict:
"""通过名称或电子邮件查找用户。"""
if email:
return find_user_by_email(email) # 伪代码
elif name:
return find_user_by_name(name) # 伪代码
else:
return {"error": "No lookup parameters provided"}
# 手动将多个装饰器应用于同一函数
mcp.resource("users://email/{email}")(lookup_user)
mcp.resource("users://name/{name}")(lookup_user)
现在LLM或客户端可以通过两种不同的方式检索用户信息:
- users://email/alice@example.com → 通过电子邮件查找用户(name=None)
- users://name/Bob → 通过名称查找用户(email=None)
这种方法允许单个函数注册多个URI模式,同时保持实现干净和直接。
模板提供了一种强大的方式来公开参数化的数据访问点,遵循类似REST的原则。
十四、错误处理
新版本2.4.1功能
如果您的资源函数遇到错误,您可以引发标准Python异常(ValueError、TypeError、FileNotFoundError、自定义异常等)或FastMCP ResourceError。
默认情况下,所有异常(包括其详细信息)都会被记录并转换为MCP错误响应发送回客户端LLM。这有助于LLM理解失败并适当反应。
如果您出于安全原因想要屏蔽内部错误详细信息,您可以:
- 在创建FastMCP实例时使用mask_error_details=True参数:
python
mcp = FastMCP(name="SecureServer", mask_error_details=True)
- 或使用ResourceError显式控制发送给客户端的错误信息:
python
from fastmcp import FastMCP
from fastmcp.exceptions import ResourceError
mcp = FastMCP(name="DataServer")
@mcp.resource("resource://safe-error")
def fail_with_details() -> str:
"""此资源提供详细的错误信息。"""
# ResourceError内容总是发送回客户端,
# 无论mask_error_details设置如何
raise ResourceError("Unable to retrieve data: file not found")
@mcp.resource("resource://masked-error")
def fail_with_masked_details() -> str:
"""当mask_error_details=True时,此资源屏蔽内部错误详细信息。"""
# 如果mask_error_details=True,此消息将被屏蔽
raise ValueError("Sensitive internal file path: /etc/secrets.conf")
@mcp.resource("data://{id}")
def get_data_by_id(id: str) -> dict:
"""模板资源也支持相同的错误处理模式。"""
if id == "secure":
raise ValueError("Cannot access secure data")
elif id == "missing":
raise ResourceError("Data ID 'missing' not found in database")
return {"id": id, "value": "data"}
当mask_error_details=True时,只有来自ResourceError的错误消息会包含详细信息,其他异常将转换为通用消息。
十五、重复资源
新版本2.1.0功能
您可以配置FastMCP服务器如何处理尝试注册多个具有相同URI的资源或模板。在FastMCP初始化期间使用on_duplicate_resources设置。
python
from fastmcp import FastMCP
mcp = FastMCP(
name="ResourceServer",
on_duplicate_resources="error" # 重复时引发错误
)
@mcp.resource("data://config")
def get_config_v1(): return {"version": 1}
# 此注册尝试将引发ValueError,因为
# "data://config"已注册且行为为"error"。
# @mcp.resource("data://config")
# def get_config_v2(): return {"version": 2}
重复行为选项是:
- "warn"(默认):记录警告,新资源/模板替换旧资源/模板。
- "error":引发ValueError,防止重复注册。
- "replace":静默地用新资源/模板替换现有资源/模板。
- "ignore":保留原始资源/模板并忽略新的注册尝试。