你知道吗?GitHub 上有一个 25,548 Stars 的 Python 框架,每天被下载 一百万次,承载了 70% 的 MCP Server。这就是 PrefectHQ/fastmcp,在 2026 年已经是 Python 圈做 Model Context Protocol(MCP)的事实标准。
很多人对 FastMCP 的印象还停留在「@mcp.tool 一行装饰器 + mcp.run() 起服务」这个最小可用版本。但今天的 FastMCP 3.0 已经是一个完整的生产级框架——自带客户端、自带 OpenAPI 自动转 MCP、自带多模态类型支持、自带托管 Cloud 部署。本文要分享的 5 个隐藏用法,每个都能让一段 200 行的脚本变成可上线的服务。
背景段。 在 2026 年,Model Context Protocol 已经是 AI Agent 与外部系统对接的事实层。Claude Code、Cursor、Goose、Cline、OpenHands 这些主流 agent 框架都内置 MCP 客户端。而服务端这一侧,FastMCP 不再只是个「写工具的库」——它已经进化成「写工具 + 部署工具 + 监控工具 + 工具治理」的全套解决方案。2026 年 3 月 HN 上「Welcome to FastMCP」这篇获得了 80 票 / 68 条评论,FastMCP 3.0 GA 也在 2 月登上了 HN 首页。但绝大多数教程都还停留在介绍 mcp.tool() 这一层。下面这 5 个用法,README 里其实都有,但埋得比较深。
隐藏用法 #1:Elicitation —— 让 LLM 在工具执行中向用户提问
大多数人的做法:把工具的所有参数都列在函数签名上,然后 LLM 一旦漏传就崩;或者干脆再写一个 ask_user 工具手动路由对话。
隐藏技巧:FastMCP 2.13+ 在 Context 对象里内置了 ctx.elicit()。一个工具可以暂停自己的执行,给客户端发送一个结构化问题,等用户回答后继续执行。而且响应的 schema 直接由类型决定——传一个 @dataclass,客户端自动渲染成表单。
from dataclasses import dataclass
from fastmcp import Context, FastMCP
mcp = FastMCP("Elicitation Demo")
@mcp.tool
async def plan_dinner(ctx: Context) -> str:
"""询问用户晚餐偏好,然后规划菜单。"""
@dataclass
class DinnerPrefs:
cuisine: str # 菜系
vegetarian: bool # 是否素食
result = await ctx.elicit(
"今晚想吃什么?",
response_type=DinnerPrefs,
)
if result.action == "accept":
prefs = result.data
veg = "素" if prefs.vegetarian else ""
return f"今晚菜单:一份精致的{veg}{prefs.cuisine}!"
return "那就下次再说啦!"
效果:一个工具就完成了多轮交互。Claude Desktop 或 Cursor 客户端会用 DinnerPrefs 的形状渲染原生表单,LLM 看不到这个问题,工具返回值依然是 str。不需要再写并行的 ask_user 工具。
数据来源:PrefectHQ/fastmcp examples/elicitation.py(2026-06-09 在 main 分支验证通过);HN 「Welcome to FastMCP」帖子 80 票(2026-03-24,objectID 47508149)。
隐藏用法 #2:Image 返回类型 —— 让 LLM 真正「看见」图片
大多数人的做法:用 Pillow 截图后 base64 编码,返回一个 JSON 字符串,然后告诉模型「这是一张图片」。接着花一小时调试为什么模型说「我看到的是一长串字符」。
隐藏技巧:FastMCP 定义了 fastmcp.utilities.types.Image(以及 Audio 和 File)。返回这些类型时,MCP 传输层会原生以多模态内容块的形式发送字节数据,模型收到的是真正的像素。
import io
import pyautogui
from fastmcp import FastMCP
from fastmcp.utilities.types import Image
mcp = FastMCP("Screenshot Demo")
@mcp.tool
def take_screenshot() -> Image:
"""截取用户屏幕并返回图像。任何时候用户想让你看屏幕时调用此工具。"""
buffer = io.BytesIO()
screenshot = pyautogui.screenshot()
# 文件超过 ~1MB 时 Claude 端会拒收,所以转 JPEG 压缩
screenshot.convert("RGB").save(buffer, format="JPEG", quality=60, optimize=True)
return Image(data=buffer.getvalue(), format="jpeg")
效果:Claude Desktop 会直接渲染截图,模型能描述屏幕上显示的内容。如果改用 bytes 或 data: URL 字符串,要么客户端报错,要么模型幻觉成「一段长字符串」。
数据来源:PrefectHQ/fastmcp examples/screenshot.py(2026-06-09 在 main 分支验证通过);fastmcp.utilities.types 模块提供 Image / Audio / File 三种原生多模态载体。
隐藏用法 #3:CodeMode —— 把 50 个工具折叠成 2 个元工具
大多数人的做法:写一个有 40 个 @mcp.tool 函数的服务,盯着 LLM 在 60K 上下文中枚举工具列表的 token 消耗发呆,最后不得不给工具描述加 RAG 检索来省 token。
隐藏技巧:FastMCP 自带实验性 CodeMode 转换器,能把整个工具目录替换成两个元工具——search(按关键词发现工具)和 execute(在沙箱里跑一段 Python 来串起真实工具调用)。模型写一次 Python 调用就够了。
from fastmcp import FastMCP
from fastmcp.experimental.transforms.code_mode import CodeMode
mcp = FastMCP("CodeMode Demo")
@mcp.tool
def list_files(directory: str) -> list[str]:
"""列出目录下的文件。"""
import os
return os.listdir(directory)
@mcp.tool
def read_file(path: str) -> str:
"""读取文件内容。"""
with open(path) as f:
return f.read()
# CodeMode 把上面 8 个工具折叠成 `search` + `execute` 两个元工具。
# LLM 通过关键词搜索发现工具,然后写 Python 脚本在 pydantic-monty 沙箱里链式调用。
mcp.add_transform(CodeMode())
if __name__ == "__main__":
mcp.run()
安装后(pip install "fastmcp[code-mode]"),LLM 看到的只有 search_code_mode 和 execute_code_mode。一个「找出 /repo 下最大的 .py 文件」请求,变成一次 execute 调用执行 max((f for f in __list_files('/repo') if f.endswith('.py')), key=lambda p: __read_file(f).__len__())。
效果:40 个工具的服务变成 2 个工具的接口,LLM 用它本来就会的 Python,调用轮数从 N 次降到 1 次。沙箱是 pydantic-monty,用 Rust 实现的 Python 解释器,不是 subprocess 黑魔法。
数据来源:PrefectHQ/fastmcp examples/code_mode/server.py(2026-06-09 验证通过);fastmcp.experimental.transforms.code_mode 模块随 FastMCP 2.x 引入。
隐藏用法 #4:后台任务 task=True —— 长时工具断线后能续上
大多数人的做法:写一个跑 10 分钟的工具,看着 MCP 请求在 60 秒超时,然后手撸一个轮询接口 + 自维护 Redis 队列。
隐藏技巧:FastMCP 与 Docket(一个 Redis 后端的任务队列)深度集成,通过 task=True 装饰器开关启用。工具可以异步运行,通过 Progress 依赖上报进度,客户端断线重连也不丢状态。
import asyncio
from typing import Annotated
from docket import Logged
from fastmcp import FastMCP
from fastmcp.dependencies import Progress
mcp = FastMCP("Tasks Example")
@mcp.tool(task=True)
async def slow_computation(
duration: Annotated[int, Logged],
progress: Progress = Progress(),
) -> str:
"""执行一个耗时 `duration` 秒的计算。"""
if duration < 1 or duration > 60:
raise ValueError("duration 必须在 1 到 60 秒之间")
await progress.set_total(duration)
for i in range(duration):
await asyncio.sleep(1)
await progress.increment()
await progress.set_message(
f"工作中... {i+1}/{duration}秒(剩 {duration-i-1} 秒)"
)
return f"耗时 {duration} 秒完成"
效果:客户端拿到一个 task handle,在同一个 MCP 连接上轮询进度,worker 跑完后拿到最终返回值。同一个装饰器对 CPU 密集型(offload 给 worker)和 I/O 密集型(保留在事件循环)都生效。task_elicitation.py 这个示例更进一步——展示 ctx.elicit() 在后台任务中工作:5 分钟的任务跑到第 30 秒时向用户提问,用户回答后任务继续。
数据来源:PrefectHQ/fastmcp examples/tasks/server.py + examples/task_elicitation.py(2026-06-09 验证通过);HN 「FastMCP 3.0 Is GA」帖子(2026-02-18,objectID 47068067)。
隐藏用法 #5:Server Composition —— 用 mount 拼装多个服务
大多数人的做法:把多个服务的工具复制粘贴到一个「全家桶」服务里,然后每次改一个工具签名都要同步改五份文件。最后得到五个 200 行的重复大服务。
隐藏技巧:FastMCP 服务是一等公民对象。mount(prefix, subserver) 把 subserver 的所有工具、资源、提示词挂载到父服务的一个命名空间前缀下;import_server(subserver) 做同样的事但不加前缀(用于直接合并)。两种方式都跨传输生效——可以把本地内存子服务挂到 stdio 父服务下,也可以把远程 HTTP 服务挂到 streamable-http 父服务下。
import asyncio
from fastmcp import FastMCP
# 子服务 A:git 工具
git_server = FastMCP("Git 工具")
@git_server.tool
def git_status(repo_path: str) -> str:
"""返回某个 repo 的 `git status --short` 结果。"""
import subprocess
return subprocess.check_output(
["git", "-C", repo_path, "status", "--short"], text=True
)
# 子服务 B:文件工具
file_server = FastMCP("文件工具")
@file_server.tool
def read_file(path: str) -> str:
"""读取 UTF-8 文本文件。"""
return open(path, encoding="utf-8").read()
# 父服务通过 mount 组合两个子服务
main = FastMCP("主服务")
main.mount("git", git_server)
main.mount("fs", file_server)
# 合并后的服务对外暴露:
# - git.git_status(repo_path)
# - fs.read_file(path)
# 一个工具目录、一道权限边界、一个部署目标。
if __name__ == "__main__":
asyncio.run(main.run_async())
效果:三个小而专、可独立测试的服务折叠成一个有命名空间的工具目录。重新部署某个子工具只需要重启对应的子服务(HTTP 挂载时父服务无需改代码)。同样的手法可以把第三方托管服务(比如 Notion MCP Server)组合进你自己的工具集,而不需要 fork。
数据来源:PrefectHQ/fastmcp examples/mount_example.py(2026-06-09 验证通过);FastMCP.mount() 和 FastMCP.import_server() 在官方 server composition 文档中介绍。
总结:2026 年 FastMCP 的 5 个隐藏用法
-
Elicitation ——
await ctx.elicit(prompt, response_type=Dataclass)让工具在执行中向用户问结构化问题,再继续执行。无需并行的ask_user工具。 -
Image / Audio / File 返回类型 —— 返回
fastmcp.utilities.types.Image(或Audio/File)原生以多模态内容发送,而不是把 base64 塞进字符串。 -
CodeMode 转换器 —— 把 N 个工具折叠成
search+execute一对元工具,背后是 pydantic-monty 沙箱。降低调用轮数和上下文窗口压力。 -
后台任务(
task=True) —— 长时工具断线可续、能上报Progress,并且能在任务中途调用ctx.elicit()。由 Docket 驱动。 -
Server Composition ——
mount(prefix, subserver)和import_server(subserver)把多个独立维护的服务缝合成一个工具目录。
如果你想深入了解更广的 MCP 生态,这几篇过去的 Dev.to 文章值得一读:
- MCP Registry's 5 Hidden Uses Nobody Talks About in 2026
- MCP Python SDK's 5 Hidden Uses Nobody Is Talking About in 2026
- Goose's 5 Hidden Uses That Turn It Into a Production AI Agent Stack in 2026
你在生产环境用 FastMCP 的哪个隐藏用法?欢迎在评论区留言——我会在后续文章里挑出最有意思的几个深入展开。
Top comments (0)