FastMCP 2.0 服务端开发教学文档(下)

本文为 FastMCP 2.0 服务端教学文档的下篇,重点讲解 上下文(Context)代理与组合交互机制 (征询、日志、进度、采样)、中间件与存储后端 等高阶能力,帮助开发者构建可扩展、交互式、生产就绪的 MCP 服务。


一、MCP 上下文(Context)

上下文对象是 FastMCP 中连接工具、资源、提示函数与 MCP 协议运行时功能的桥梁。通过 依赖注入运行时依赖函数,开发者可在任意函数中访问丰富的能力。

1.1 依赖注入方式(推荐)

在函数签名中增加类型为 Context 的参数,FastMCP 自动注入:

python 复制代码
from fastmcp import FastMCP, Context

@mcp.tool
async def demo_tool(ctx: Context) -> str:
    await ctx.info("工具正在执行")
    return "完成"

参数名可任意(ctx, context, _c 均可),只需类型提示为 Context

1.2 运行时依赖函数(适用于深层调用栈)

python 复制代码
from fastmcp import get_context

def inner_function():
    ctx = get_context()  # 仅在请求上下文中有效
    ctx.info("从嵌套函数中记录日志")

⚠️ 注意get_context() 仅限服务器端在活跃请求中调用,否则抛出 RuntimeError

1.3 上下文核心能力

功能 方法 说明
日志记录 ctx.debug/info/warning/error(msg) 向客户端发送日志(LLM 可见)
进度报告 ctx.report_progress(progress, total=None) 支持百分比、绝对值、多阶段等模式
资源访问 await ctx.read_resource(uri) 读取本服务器或其他服务器注册的资源
LLM 采样 await ctx.sample(prompt, ...) 请求 LLM 生成文本(如摘要、转换)
用户征询 await ctx.elicit(message, response_type=...) 交互式请求用户输入结构化数据
状态管理 ctx.set_state(key, value) / ctx.get_state(key) 在中间件与工具间传递数据
请求元数据 ctx.request_id, ctx.client_id, ctx.session_id 识别当前会话
服务器实例 ctx.fastmcp 访问当前 FastMCP 实例
变更通知 ctx.notify_tools_changed() 手动触发能力列表变更通知

二、代理(Proxy)与服务器组合(Composition)

2.1 代理服务器(Proxy)

代理允许将远程或本地 MCP 服务器透明地整合到当前服务中。

创建远程代理
python 复制代码
from fastmcp import FastMCP

# 代理远程 MCP 服务器
weather_proxy = FastMCP.as_proxy(
    name="WeatherProxy",
    transport="http",
    url="https://weather-api.example.com/mcp"
)

# 挂载到主服务器
main = FastMCP("MainServer")
main.mount(weather_proxy, prefix="weather")
创建配置驱动代理(MCPConfig)
python 复制代码
config = {
    "servers": [
        {
            "name": "GitHub",
            "transport": "http",
            "url": "https://github-mcp.example.com/mcp"
        }
    ]
}
proxy = FastMCP.as_proxy_from_config(config)
代理支持图标与元信息
python 复制代码
from mcp.types import Icon

mcp = FastMCP(
    name="WeatherService",
    website_url="https://weather.example.com",
    icons=[
        Icon(
            src="https://weather.example.com/icon-48.png",
            mimeType="image/png",
            sizes=["48x48"]
        ),
        Icon(
            src="https://weather.example.com/icon-96.png",
            mimeType="image/png",
            sizes=["96x96"]
        ),
    ]
)

2.2 服务器组合(Composition)

FastMCP 支持两种组合模式:静态导入import_server) 与 动态挂载mount)。

特性 import_server() mount()
类型 静态拷贝 动态代理
更新同步 ❌ 导入后不更新 ✅ 实时同步
性能 ⚡ 快(直接调用) ⏱️ 较慢(委托调用)
生命周期 不执行子服务器生命周期 执行(代理模式)
适用场景 打包发布、性能敏感 开发调试、模块热插拔
示例:静态导入
python 复制代码
# 子服务器
weather = FastMCP("Weather")
@weather.tool
def get_temp(city: str) -> float: ...

# 主服务器
main = FastMCP("Main")
await main.import_server(weather, prefix="weather")
# 工具名变为 weather_get_temp
示例:动态挂载
python 复制代码
main.mount(weather, prefix="weather")  # 实时链接
前缀规则
  • 工具/提示{prefix}_{name}
  • 资源 URI (路径格式,默认):data://{prefix}/info
  • 资源 URI (协议格式):{prefix}+//info(需配置)
标签递归过滤(≥2.9.0)
python 复制代码
# 子服务器
weather = FastMCP("Weather", include_tags={"prod"})
@weather.tool(tags={"prod", "free"})
def free_api(): ...

@weather.tool(tags={"prod", "premium"})
def premium_api(): ...

# 主服务器
main = FastMCP("Main", exclude_tags={"premium"})
main.mount(weather)  # 仅暴露 free_api

三、高级交互机制

3.1 用户征询(Elicitation)

允许工具在执行中暂停并请求用户输入,支持标量、结构化、选项式输入。

基本用法
python 复制代码
from dataclasses import dataclass

@dataclass
class Task:
    title: str
    priority: Literal["低", "中", "高"]

@mcp.tool
async def create_task(ctx: Context) -> str:
    result = await ctx.elicit("请填写任务信息", response_type=Task)
    if result.action == "accept":
        return f"创建任务:{result.data.title}"
    return "已取消"
支持的响应类型
类型 说明
str, int, bool 自动包装为 {value: T},返回时解包
None 仅确认/拒绝,无数据
["A", "B"] 快捷选项(等价于 Literal["A","B"]
dataclass / Pydantic 结构化对象(仅支持标量字段)
多轮征询
python 复制代码
@mcp.tool
async def plan_meeting(ctx: Context) -> str:
    title = await ctx.elicit("标题?", str)
    if title.action != "accept": return "取消"
    duration = await ctx.elicit("时长(分钟)?", int)
    return f"会议:{title.data}({duration.data}分钟)"

⚠️ 客户端要求 :必须实现 elicitation 处理程序,否则抛出错误。


3.2 日志记录(Logging)

向客户端发送结构化日志,LLM 可据此调整行为。

python 复制代码
@mcp.tool
async def risky_operation(ctx: Context) -> str:
    await ctx.info("开始高风险操作")
    try:
        # ...
        await ctx.info("操作成功")
    except Exception as e:
        await ctx.error(f"操作失败: {e}")
        raise

日志级别:debug < info < warning < error


3.3 进度报告(Progress)

提升长时间操作的用户体验,防止超时。

进度模式
模式 用法 示例
绝对进度 progress=i, total=n 处理 5/10 个文件
百分比 progress=75.5, total=100 下载 75.5%
不确定 progress=count(无 total) 扫描未知数量文件
多阶段 分段报告 0--25%、25--60% 等 数据迁移
示例:多阶段迁移
python 复制代码
@mcp.tool
async def data_migration(src: str, dst: str, ctx: Context) -> str:
    # 阶段1:验证 (0-25%)
    for i in range(5):
        await ctx.report_progress(i * 5, 100)
    # 阶段2:导出 (25-60%)
    for i in range(7):
        await ctx.report_progress(25 + i * 5, 100)
    # ... 其他阶段
    await ctx.report_progress(100, 100)
    return "迁移完成"

⚠️ 客户端要求 :必须在初始请求中提供 progressToken,否则进度调用静默忽略。


3.4 LLM 采样(Sampling)

在工具内部调用 LLM 生成文本,实现 AI 增强逻辑。

基本用法
python 复制代码
@mcp.tool
async def summarize_text(text: str, ctx: Context) -> str:
    summary = await ctx.sample(f"用一句话总结:{text[:500]}")
    return summary.text
高级用法
python 复制代码
# 系统提示 + 模型偏好
summary = await ctx.sample(
    messages=[
        {"role": "system", "content": "你是一个专业摘要助手"},
        {"role": "user", "content": text}
    ],
    model_preferences={"family": "claude"}
)
回退处理器(处理不支持采样的客户端)
python 复制代码
from fastmcp.llm import OpenAISamplingHandler

mcp = FastMCP(
    "MyServer",
    sampling_handler=OpenAISamplingHandler(api_key="..."),
    sampling_handler_behavior="fallback"  # 客户端不支持时自动回退
)
  • "fallback":仅客户端不支持时使用回退
  • "always":始终使用回退,绕过客户端

四、中间件(Middleware)与存储后端(Storage Backends)

4.1 中间件系统

FastMCP 支持通过中间件扩展服务器行为,如缓存、认证、日志等。

响应缓存中间件
python 复制代码
from fastmcp.server.middleware.caching import ResponseCachingMiddleware
from key_value.aio.stores.disk import DiskStore

mcp.add_middleware(ResponseCachingMiddleware(
    cache_storage=DiskStore(directory="/tmp/fastmcp-cache")
))

缓存基于请求内容哈希,自动跳过副作用工具(如非 idempotent 工具)。

4.2 可插拔存储后端(≥2.13.0)

FastMCP 使用 py-key-value-aio 库提供统一存储接口。

后端 适用场景 特点
MemoryStore 开发、测试 快速,重启丢失
DiskStore 单机生产 持久化,需文件系统
RedisStore 分布式生产 高可用,支持 TTL
DynamoDB/MongoDB 云原生 与现有基础设施集成
OAuth 令牌安全存储(生产必需)
python 复制代码
from key_value.aio.stores.redis import RedisStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet

auth = GitHubProvider(
    client_storage=FernetEncryptionWrapper(
        key_value=RedisStore(host="redis.example.com"),
        fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
    )
)

⚠️ 安全警告 :生产环境必须使用 FernetEncryptionWrapper 加密 OAuth 令牌,否则以明文存储!

客户端令牌持久化
python 复制代码
from fastmcp.client.auth import OAuthClientProvider
from key_value.aio.stores.disk import DiskStore

client = OAuthClientProvider(
    mcp_url="...",
    token_storage=DiskStore(directory="~/.fastmcp/tokens")
)

五、最佳实践总结

场景 推荐方案
跨服务器调用 使用 ctx.read_resource() 或组合(mount/import
交互式工具 结合 ctx.elicit() + ctx.report_progress()
AI 增强 使用 ctx.sample() + 回退处理器
性能敏感 import_server() 代替 mount()
生产部署 Redis + 加密存储 + 严格输入验证
模块化开发 按功能拆分服务器(WeatherServer, DBServer),通过组合集成
安全错误处理 mask_error_details=True + ToolError/ResourceError

相关推荐
楚来客20 小时前
AI基础概念之八:Transformer算法通俗解析
人工智能·算法·transformer
XTTX11020 小时前
Vue3+Cesium教程(36)--动态设置降雨效果
前端·javascript·vue.js
效率客栈老秦20 小时前
Python Trae提示词开发实战(8):数据采集与清洗一体化方案让效率提升10倍
人工智能·python·ai·提示词·trae
哈里谢顿20 小时前
一条 Python 语句在 C 扩展里到底怎么跑
python
znhy_2320 小时前
day46打卡
python
小和尚同志20 小时前
虽然 V0 很强大,但是ScreenshotToCode 依旧有市场
人工智能·aigc
HyperAI超神经20 小时前
【vLLM 学习】Rlhf
人工智能·深度学习·学习·机器学习·vllm
芯盾时代20 小时前
石油化工行业网络风险解决方案
网络·人工智能·信息安全
线束线缆组件品替网20 小时前
Weidmüller 工业以太网线缆技术与兼容策略解析
网络·人工智能·电脑·硬件工程·材料工程