fastmcp
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFastMCP - Build MCP Servers in Python
FastMCP - 在Python中构建MCP服务器
FastMCP is a Python framework for building Model Context Protocol (MCP) servers that expose tools, resources, and prompts to Large Language Models like Claude. This skill provides production-tested patterns, error prevention, and deployment strategies for building robust MCP servers.
FastMCP是一个Python框架,用于构建模型上下文协议(MCP)服务器,向Claude等大语言模型(LLM)暴露工具、资源和提示词。本技能提供经过生产验证的模式、错误预防方案以及部署策略,帮助你构建稳健的MCP服务器。
Quick Start
快速开始
Installation
安装
bash
pip install fastmcpbash
pip install fastmcpor
或
uv pip install fastmcp
undefineduv pip install fastmcp
undefinedMinimal Server
最简服务器示例
python
from fastmcp import FastMCPpython
from fastmcp import FastMCPMUST be at module level for FastMCP Cloud
必须在模块级别定义,以支持FastMCP Cloud
mcp = FastMCP("My Server")
@mcp.tool()
async def hello(name: str) -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if name == "main":
mcp.run()
**Run it:**
```bashmcp = FastMCP("My Server")
@mcp.tool()
async def hello(name: str) -> str:
"""向某人打招呼。"""
return f"Hello, {name}!"
if name == "main":
mcp.run()
**运行方式:**
```bashLocal development
本地开发
python server.py
python server.py
With FastMCP CLI
使用FastMCP CLI
fastmcp dev server.py
fastmcp dev server.py
HTTP mode
HTTP模式
python server.py --transport http --port 8000
undefinedpython server.py --transport http --port 8000
undefinedWhat's New in v2.14.x (December 2025)
v2.14.x版本新特性(2025年12月)
v2.14.2 (December 31, 2024)
v2.14.2(2024年12月31日)
- MCP SDK pinned to <2.x for compatibility
- Supabase provider gains parameter
auth_route - Bug fixes: outputSchema resolution, OAuth Proxy validation, OpenAPI 3.1 support
$ref
- MCP SDK固定为<2.x版本以保证兼容性
- Supabase提供新增参数
auth_route - 修复问题:outputSchema的解析、OAuth代理验证、OpenAPI 3.1支持
$ref
v2.14.1: Sampling with Tools (SEP-1577)
v2.14.1:工具调用采样(SEP-1577)
- now accepts tools for agentic workflows
ctx.sample() - promoted from experimental
AnthropicSamplingHandler - for single LLM call returning
ctx.sample_step()SampleStep - Python 3.13 support added
- 现在支持传入工具,适用于智能体工作流
ctx.sample() - 从实验特性转为正式特性
AnthropicSamplingHandler - 用于单次LLM调用,返回
ctx.sample_step()对象SampleStep - 新增Python 3.13支持
v2.14.0: Background Tasks (SEP-1686)
v2.14.0:后台任务(SEP-1686)
- Protocol-native background tasks for long-running operations
- Add to async decorators; progress tracking without blocking
task=True - MCP 2025-11-25 specification support
- SEP-1699: SSE polling and event resumability
- SEP-1330: Multi-select enum elicitation schemas
- SEP-1034: Default values for elicitation schemas
⚠️ Breaking Changes (v2.14.0):
- module removed (use
BearerAuthProviderorJWTVerifier)OAuthProxy - method removed
Context.get_http_request() - top-level import removed (use
fastmcp.Image)from fastmcp.utilities import Image - ,
enable_docketsettings removed (always enabled)enable_tasks - ,
run_streamable_http_async(),sse_app(),streamable_http_app()methods removedrun_sse_async() - parameter removed from decorators
dependencies - support eliminated
output_schema=False - environment variable prefix deprecated
FASTMCP_SERVER_
Known Compatibility:
- MCP SDK pinned to <2.x (v2.14.2+)
- 协议原生后台任务,支持长时间运行的操作
- 异步装饰器添加参数;支持进度追踪且不阻塞主线程
task=True - 支持MCP 2025-11-25规范
- SEP-1699:SSE轮询和事件可恢复性
- SEP-1330:多选枚举启发式模式
- SEP-1034:启发式模式默认值
⚠️ v2.14.0破坏性变更:
- 移除模块(使用
BearerAuthProvider或JWTVerifier替代)OAuthProxy - 移除方法
Context.get_http_request() - 移除顶层导入(使用
fastmcp.Image替代)from fastmcp.utilities import Image - 移除、
enable_docket配置项(默认始终启用)enable_tasks - 移除、
run_streamable_http_async()、sse_app()、streamable_http_app()方法run_sse_async() - 移除装饰器中的参数
dependencies - 取消对的支持
output_schema=False - 弃用环境变量前缀
FASTMCP_SERVER_
已知兼容性:
- MCP SDK固定为<2.x版本(v2.14.2及以上)
What's New in v3.0.0 (Beta - January 2026)
v3.0.0版本新特性(测试版 - 2026年1月)
⚠️ MAJOR BREAKING CHANGES - FastMCP 3.0 is a complete architectural refactor.
⚠️ 重大破坏性变更 - FastMCP 3.0是一次完整的架构重构。
Provider Architecture
提供器架构
All components now sourced via Providers:
- - Discover decorated functions from directories with hot-reload
FileSystemProvider - - Expose agent skill files as MCP resources
SkillsProvider - - Auto-generate from OpenAPI specs
OpenAPIProvider - - Proxy to remote MCP servers
ProxyProvider
python
from fastmcp import FastMCP
from fastmcp.providers import FileSystemProvider
mcp = FastMCP("server")
mcp.add_provider(FileSystemProvider(path="./tools", reload=True))所有组件现在通过提供器获取:
- - 从目录中发现装饰函数,支持热重载
FileSystemProvider - - 将智能体技能文件暴露为MCP资源
SkillsProvider - - 从OpenAPI规范自动生成
OpenAPIProvider - - 代理到远程MCP服务器
ProxyProvider
python
from fastmcp import FastMCP
from fastmcp.providers import FileSystemProvider
mcp = FastMCP("server")
mcp.add_provider(FileSystemProvider(path="./tools", reload=True))Transforms (Component Middleware)
转换(组件中间件)
Modify components without changing source code:
- Namespace, rename, filter by version
- - Expose resources as tools
ResourcesAsTools - - Expose prompts as tools
PromptsAsTools
python
from fastmcp.transforms import Namespace, VersionFilter
mcp.add_transform(Namespace(prefix="api"))
mcp.add_transform(VersionFilter(min_version="2.0"))无需修改源代码即可修改组件:
- 命名空间、重命名、按版本过滤
- - 将资源暴露为工具
ResourcesAsTools - - 将提示词暴露为工具
PromptsAsTools
python
from fastmcp.transforms import Namespace, VersionFilter
mcp.add_transform(Namespace(prefix="api"))
mcp.add_transform(VersionFilter(min_version="2.0"))Component Versioning
组件版本控制
python
@mcp.tool(version="2.0")
async def fetch_data(query: str) -> dict:
# Clients see highest version by default
# Can request specific version
return {"data": [...]}python
@mcp.tool(version="2.0")
async def fetch_data(query: str) -> dict:
# 客户端默认使用最高版本
# 也可以请求特定版本
return {"data": [...]}Session-Scoped State
会话级状态
python
@mcp.tool()
async def set_preference(key: str, value: str, ctx: Context) -> dict:
await ctx.set_state(key, value) # Persists across session
return {"saved": True}
@mcp.tool()
async def get_preference(key: str, ctx: Context) -> dict:
value = await ctx.get_state(key, default=None)
return {"value": value}python
@mcp.tool()
async def set_preference(key: str, value: str, ctx: Context) -> dict:
await ctx.set_state(key, value) # 在会话中持久化
return {"saved": True}
@mcp.tool()
async def get_preference(key: str, ctx: Context) -> dict:
value = await ctx.get_state(key, default=None)
return {"value": value}Other Features
其他特性
- flag for auto-restart during development
--reload - Automatic threadpool dispatch for sync functions
- Tool timeouts
- OpenTelemetry tracing
- Component authorization:
@tool(auth=require_scopes("admin"))
- 标志,开发时自动重启服务器
--reload - 自动为同步函数分配线程池
- 工具超时机制
- OpenTelemetry追踪
- 组件授权:
@tool(auth=require_scopes("admin"))
Migration Guide
迁移指南
Pin to v2 if not ready:
undefined若未准备好升级,固定到v2版本:
undefinedrequirements.txt
requirements.txt
fastmcp<3
**For most servers**, updating the import is all you need:
```pythonfastmcp<3
**对于大多数服务器**,只需更新导入语句即可兼容:
```pythonv2.x and v3.0 compatible
兼容v2.x和v3.0版本
from fastmcp import FastMCP
mcp = FastMCP("server")
from fastmcp import FastMCP
mcp = FastMCP("server")
... rest of code works the same
... 其余代码保持不变
**See**: [Official Migration Guide](https://github.com/jlowin/fastmcp/blob/main/docs/development/upgrade-guide.mdx)
---
**参考**: [官方迁移指南](https://github.com/jlowin/fastmcp/blob/main/docs/development/upgrade-guide.mdx)
---Core Concepts
核心概念
Tools
工具
Functions LLMs can call. Best practices: Clear names, comprehensive docstrings (LLMs read these!), strong type hints (Pydantic validates), structured returns, error handling.
python
@mcp.tool()
async def async_tool(url: str) -> dict: # Use async for I/O
async with httpx.AsyncClient() as client:
return (await client.get(url)).json()LLM可以调用的函数。最佳实践:清晰的命名、全面的文档字符串(LLM会读取这些内容!)、强类型提示(Pydantic会验证)、结构化返回、错误处理。
python
@mcp.tool()
async def async_tool(url: str) -> dict: # I/O操作使用async
async with httpx.AsyncClient() as client:
return (await client.get(url)).json()Resources
资源
Expose data to LLMs. URI schemes: , , , , , or custom.
data://file://resource://info://api://python
@mcp.resource("user://{user_id}/profile") # Template with parameters
async def get_user(user_id: str) -> dict: # CRITICAL: param names must match
return await fetch_user_from_db(user_id)向LLM暴露数据。URI方案:、、、、或自定义方案。
data://file://resource://info://api://python
@mcp.resource("user://{user_id}/profile") # 带参数的模板
async def get_user(user_id: str) -> dict: # 关键:参数名称必须与模板匹配
return await fetch_user_from_db(user_id)Prompts
提示词
Pre-configured prompts with parameters.
python
@mcp.prompt("analyze")
def analyze_prompt(topic: str) -> str:
return f"Analyze {topic} considering: state, challenges, opportunities, recommendations."带参数的预配置提示词。
python
@mcp.prompt("analyze")
def analyze_prompt(topic: str) -> str:
return f"分析{topic},需涵盖:现状、挑战、机遇、建议。"Context Features
上下文特性
Inject parameter (with type hint!) for advanced features:
ContextElicitation (User Input):
python
from fastmcp import Context
@mcp.tool()
async def confirm_action(action: str, context: Context) -> dict:
confirmed = await context.request_elicitation(prompt=f"Confirm {action}?", response_type=str)
return {"status": "completed" if confirmed.lower() == "yes" else "cancelled"}Progress Tracking:
python
@mcp.tool()
async def batch_import(file_path: str, context: Context) -> dict:
data = await read_file(file_path)
for i, item in enumerate(data):
await context.report_progress(i + 1, len(data), f"Importing {i + 1}/{len(data)}")
await import_item(item)
return {"imported": len(data)}Sampling (LLM calls from tools):
python
@mcp.tool()
async def enhance_text(text: str, context: Context) -> str:
response = await context.request_sampling(
messages=[{"role": "user", "content": f"Enhance: {text}"}],
temperature=0.7
)
return response["content"]注入参数(必须带类型提示!)以使用高级特性:
Context启发式交互(用户输入):
python
from fastmcp import Context
@mcp.tool()
async def confirm_action(action: str, context: Context) -> dict:
confirmed = await context.request_elicitation(prompt=f"确认执行{action}?", response_type=str)
return {"status": "已完成" if confirmed.lower() == "yes" else "已取消"}进度追踪:
python
@mcp.tool()
async def batch_import(file_path: str, context: Context) -> dict:
data = await read_file(file_path)
for i, item in enumerate(data):
await context.report_progress(i + 1, len(data), f"正在导入第{i + 1}/{len(data)}项")
await import_item(item)
return {"已导入": len(data)}采样(从工具中调用LLM):
python
@mcp.tool()
async def enhance_text(text: str, context: Context) -> str:
response = await context.request_sampling(
messages=[{"role": "user", "content": f"优化以下文本:{text}"}],
temperature=0.7
)
return response["content"]Background Tasks (v2.14.0+)
后台任务(v2.14.0+)
Long-running operations that report progress without blocking clients. Uses Docket task scheduler (always enabled in v2.14.0+).
Basic Usage:
python
@mcp.tool(task=True) # Enable background task mode
async def analyze_large_dataset(dataset_id: str, context: Context) -> dict:
"""Analyze large dataset with progress tracking."""
data = await fetch_dataset(dataset_id)
for i, chunk in enumerate(data.chunks):
# Report progress to client
await context.report_progress(
current=i + 1,
total=len(data.chunks),
message=f"Processing chunk {i + 1}/{len(data.chunks)}"
)
await process_chunk(chunk)
return {"status": "complete", "records_processed": len(data)}Task States: → → / /
pendingrunningcompletedfailedcancelledWhen to Use:
- Operations taking >30 seconds (LLM timeout risk)
- Batch processing with per-item status updates
- Operations that may need user input mid-execution
- Long-running API calls or data processing
Known Limitation (v2.14.x):
- from
statusMessageis not forwarded to clients during background task polling (GitHub Issue #2904)ctx.report_progress() - Progress messages appear in server logs but not in client UI
- Workaround: Use official MCP SDK () instead of FastMCP for now
mcp>=1.10.0 - Status: Fix pending in PR #2906
Important: Tasks execute through Docket scheduler. Cannot execute tasks through proxies (will raise error).
支持长时间运行的操作,可报告进度且不阻塞客户端。使用Docket任务调度器(v2.14.0+默认始终启用)。
基本用法:
python
@mcp.tool(task=True) # 启用后台任务模式
async def analyze_large_dataset(dataset_id: str, context: Context) -> dict:
"""分析大型数据集并追踪进度。"""
data = await fetch_dataset(dataset_id)
for i, chunk in enumerate(data.chunks):
# 向客户端报告进度
await context.report_progress(
current=i + 1,
total=len(data.chunks),
message=f"正在处理第{i + 1}/{len(data.chunks)}个数据块"
)
await process_chunk(chunk)
return {"状态": "完成", "已处理记录数": len(data)}任务状态: (待处理)→ (运行中)→ (已完成)/ (失败)/ (已取消)
pendingrunningcompletedfailedcancelled适用场景:
- 耗时超过30秒的操作(存在LLM超时风险)
- 带逐项状态更新的批量处理
- 执行过程中可能需要用户输入的操作
- 长时间运行的API调用或数据处理
已知限制(v2.14.x):
- 中的
ctx.report_progress()在后台任务轮询时不会转发给客户端(GitHub Issue #2904)statusMessage - 进度消息会出现在服务器日志中,但不会显示在客户端UI
- 临时解决方案:暂时使用官方MCP SDK()替代FastMCP
mcp>=1.10.0 - 状态:修复方案正在PR #2906中处理
重要提示:任务通过Docket调度器执行。无法通过代理执行任务(会触发错误)。
Sampling with Tools (v2.14.1+)
工具调用采样(v2.14.1+)
Servers can pass tools to for agentic workflows where the LLM can call tools during sampling.
ctx.sample()Agentic Sampling:
python
from fastmcp import Context
from fastmcp.sampling import AnthropicSamplingHandler服务器可以将工具传递给,实现智能体工作流,让LLM在采样过程中调用工具。
ctx.sample()智能体采样示例:
python
from fastmcp import Context
from fastmcp.sampling import AnthropicSamplingHandlerConfigure sampling handler
配置采样处理器
mcp = FastMCP("Agent Server")
mcp.add_sampling_handler(AnthropicSamplingHandler(api_key=os.getenv("ANTHROPIC_API_KEY")))
@mcp.tool()
async def research_topic(topic: str, context: Context) -> dict:
"""Research a topic using agentic sampling with tools."""
# Define tools available during sampling
research_tools = [
{
"name": "search_web",
"description": "Search the web for information",
"inputSchema": {"type": "object", "properties": {"query": {"type": "string"}}}
},
{
"name": "fetch_url",
"description": "Fetch content from a URL",
"inputSchema": {"type": "object", "properties": {"url": {"type": "string"}}}
}
]
# Sample with tools - LLM can call these tools during reasoning
result = await context.sample(
messages=[{"role": "user", "content": f"Research: {topic}"}],
tools=research_tools,
max_tokens=4096
)
return {"research": result.content, "tools_used": result.tool_calls}
**Single-Step Sampling:**
```python
@mcp.tool()
async def get_single_response(prompt: str, context: Context) -> dict:
"""Get a single LLM response without tool loop."""
# sample_step() returns SampleStep for inspection
step = await context.sample_step(
messages=[{"role": "user", "content": prompt}],
temperature=0.7
)
return {
"content": step.content,
"model": step.model,
"stop_reason": step.stop_reason
}Sampling Handlers:
- - For Claude models (v2.14.1+)
AnthropicSamplingHandler - - For GPT models
OpenAISamplingHandler
Known Limitation:
works when client connects to a single server but fails with "Sampling not supported" error when multiple servers are configured in client. Tools without sampling work fine. (Community-sourced finding)
ctx.sample()mcp = FastMCP("Agent Server")
mcp.add_sampling_handler(AnthropicSamplingHandler(api_key=os.getenv("ANTHROPIC_API_KEY")))
@mcp.tool()
async def research_topic(topic: str, context: Context) -> dict:
"""使用带工具的智能体采样研究主题。"""
# 定义采样过程中可用的工具
research_tools = [
{
"name": "search_web",
"description": "在网络上搜索信息",
"inputSchema": {"type": "object", "properties": {"query": {"type": "string"}}}
},
{
"name": "fetch_url",
"description": "获取URL中的内容",
"inputSchema": {"type": "object", "properties": {"url": {"type": "string"}}}
}
]
# 带工具的采样 - LLM可以在推理过程中调用这些工具
result = await context.sample(
messages=[{"role": "user", "content": f"研究主题:{topic}"}],
tools=research_tools,
max_tokens=4096
)
return {"研究结果": result.content, "使用的工具": result.tool_calls}
**单步采样:**
```python
@mcp.tool()
async def get_single_response(prompt: str, context: Context) -> dict:
"""获取单次LLM响应,无工具循环。"""
# sample_step()返回SampleStep对象供检查
step = await context.sample_step(
messages=[{"role": "user", "content": prompt}],
temperature=0.7
)
return {
"内容": step.content,
"模型": step.model,
"停止原因": step.stop_reason
}采样处理器:
- - 用于Claude模型(v2.14.1+)
AnthropicSamplingHandler - - 用于GPT模型
OpenAISamplingHandler
已知限制:
当客户端连接到单个服务器时可以正常工作,但当客户端配置了多个服务器时会抛出“采样不支持”错误。不带采样的工具可以正常工作。(社区发现问题)
ctx.sample()Storage Backends
存储后端
Built on for OAuth tokens, response caching, persistent state.
py-key-value-aioAvailable Backends:
- Memory (default): Ephemeral, fast, dev-only
- Disk: Persistent, encrypted with , platform-aware (Mac/Windows default)
FernetEncryptionWrapper - Redis: Distributed, production, multi-instance
- Others: DynamoDB, MongoDB, Elasticsearch, Memcached, RocksDB, Valkey
Basic Usage:
python
from key_value.stores import DiskStore, RedisStore
from key_value.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet基于构建,用于存储OAuth令牌、响应缓存、持久化状态。
py-key-value-aio可用后端:
- 内存存储(默认):临时存储,速度快,仅适用于开发环境
- 磁盘存储:持久化存储,使用加密,适配不同平台(Mac/Windows默认使用)
FernetEncryptionWrapper - Redis存储:分布式存储,适用于生产环境、多实例部署
- 其他存储:DynamoDB、MongoDB、Elasticsearch、Memcached、RocksDB、Valkey
基本用法:
python
from key_value.stores import DiskStore, RedisStore
from key_value.encryption import FernetEncryptionWrapper
from cryptography.fernet import FernetDisk (persistent, single instance)
磁盘存储(持久化,单实例)
mcp = FastMCP("Server", storage=DiskStore(path="/app/data/storage"))
mcp = FastMCP("Server", storage=DiskStore(path="/app/data/storage"))
Redis (distributed, production)
Redis存储(分布式,生产环境)
mcp = FastMCP("Server", storage=RedisStore(
host=os.getenv("REDIS_HOST"), password=os.getenv("REDIS_PASSWORD")
))
mcp = FastMCP("Server", storage=RedisStore(
host=os.getenv("REDIS_HOST"), password=os.getenv("REDIS_PASSWORD")
))
Encrypted storage (recommended)
加密存储(推荐)
mcp = FastMCP("Server", storage=FernetEncryptionWrapper(
key_value=DiskStore(path="/app/data"),
fernet=Fernet(os.getenv("STORAGE_ENCRYPTION_KEY"))
))
**Platform Defaults:** Mac/Windows use Disk, Linux uses Memory. Override with `storage` parameter.mcp = FastMCP("Server", storage=FernetEncryptionWrapper(
key_value=DiskStore(path="/app/data"),
fernet=Fernet(os.getenv("STORAGE_ENCRYPTION_KEY"))
))
**平台默认值:** Mac/Windows使用磁盘存储,Linux使用内存存储。可通过`storage`参数覆盖默认设置。Server Lifespans
服务器生命周期
⚠️ Breaking Change in v2.13.0: Lifespan behavior changed from per-session to per-server-instance.
Initialize/cleanup resources once per server (NOT per session) - critical for DB connections, API clients.
python
from contextlib import asynccontextmanager
from dataclasses import dataclass
@dataclass
class AppContext:
db: Database
api_client: httpx.AsyncClient
@asynccontextmanager
async def app_lifespan(server: FastMCP):
"""Runs ONCE per server instance."""
db = await Database.connect(os.getenv("DATABASE_URL"))
api_client = httpx.AsyncClient(base_url=os.getenv("API_BASE_URL"), timeout=30.0)
try:
yield AppContext(db=db, api_client=api_client)
finally:
await db.disconnect()
await api_client.aclose()
mcp = FastMCP("Server", lifespan=app_lifespan)⚠️ v2.13.0中的破坏性变更:生命周期行为从每个会话执行一次改为每个服务器实例执行一次。
在服务器启动时初始化资源,关闭时清理资源(而非每个会话执行一次)- 这对于数据库连接、API客户端等资源至关重要。
python
from contextlib import asynccontextmanager
from dataclasses import dataclass
@dataclass
class AppContext:
db: Database
api_client: httpx.AsyncClient
@asynccontextmanager
async def app_lifespan(server: FastMCP):
"""每个服务器实例仅执行一次。"""
db = await Database.connect(os.getenv("DATABASE_URL"))
api_client = httpx.AsyncClient(base_url=os.getenv("API_BASE_URL"), timeout=30.0)
try:
yield AppContext(db=db, api_client=api_client)
finally:
await db.disconnect()
await api_client.aclose()
mcp = FastMCP("Server", lifespan=app_lifespan)Access in tools
在工具中访问
@mcp.tool()
async def query_db(sql: str, context: Context) -> list:
app_ctx = context.fastmcp_context.lifespan_context
return await app_ctx.db.query(sql)
**ASGI Integration (FastAPI/Starlette):**
```python
mcp = FastMCP("Server", lifespan=mcp_lifespan)
app = FastAPI(lifespan=mcp.lifespan) # ✅ MUST pass lifespan!State Management:
python
context.fastmcp_context.set_state(key, value) # Store
context.fastmcp_context.get_state(key, default=None) # Retrieve@mcp.tool()
async def query_db(sql: str, context: Context) -> list:
app_ctx = context.fastmcp_context.lifespan_context
return await app_ctx.db.query(sql)
**ASGI集成(FastAPI/Starlette):**
```python
mcp = FastMCP("Server", lifespan=mcp_lifespan)
app = FastAPI(lifespan=mcp.lifespan) # ✅ 必须传递lifespan!状态管理:
python
context.fastmcp_context.set_state(key, value) # 存储
context.fastmcp_context.get_state(key, default=None) # 检索Middleware System
中间件系统
8 Built-in Types: TimingMiddleware, ResponseCachingMiddleware, LoggingMiddleware, RateLimitingMiddleware, ErrorHandlingMiddleware, ToolInjectionMiddleware, PromptToolMiddleware, ResourceToolMiddleware
Execution Order (order matters!):
Request Flow:
→ ErrorHandlingMiddleware (catches errors)
→ TimingMiddleware (starts timer)
→ LoggingMiddleware (logs request)
→ RateLimitingMiddleware (checks rate limit)
→ ResponseCachingMiddleware (checks cache)
→ Tool/Resource HandlerBasic Usage:
python
from fastmcp.middleware import ErrorHandlingMiddleware, TimingMiddleware, LoggingMiddleware
mcp.add_middleware(ErrorHandlingMiddleware()) # First: catch errors
mcp.add_middleware(TimingMiddleware()) # Second: time requests
mcp.add_middleware(LoggingMiddleware(level="INFO"))
mcp.add_middleware(RateLimitingMiddleware(max_requests=100, window_seconds=60))
mcp.add_middleware(ResponseCachingMiddleware(ttl_seconds=300, storage=RedisStore()))Custom Middleware:
python
from fastmcp.middleware import BaseMiddleware
class AccessControlMiddleware(BaseMiddleware):
async def on_call_tool(self, tool_name, arguments, context):
user = context.fastmcp_context.get_state("user_id")
if user not in self.allowed_users:
raise PermissionError(f"User not authorized")
return await self.next(tool_name, arguments, context)Hook Hierarchy: (all) → / → // → (list operations)
on_messageon_requeston_notificationon_call_toolon_read_resourceon_get_prompton_list_*8种内置中间件类型: TimingMiddleware(计时中间件)、ResponseCachingMiddleware(响应缓存中间件)、LoggingMiddleware(日志中间件)、RateLimitingMiddleware(限流中间件)、ErrorHandlingMiddleware(错误处理中间件)、ToolInjectionMiddleware(工具注入中间件)、PromptToolMiddleware(提示词工具中间件)、ResourceToolMiddleware(资源工具中间件)
执行顺序(顺序至关重要!):
请求流程:
→ ErrorHandlingMiddleware(捕获错误)
→ TimingMiddleware(启动计时)
→ LoggingMiddleware(记录请求)
→ RateLimitingMiddleware(检查限流)
→ ResponseCachingMiddleware(检查缓存)
→ 工具/资源处理器基本用法:
python
from fastmcp.middleware import ErrorHandlingMiddleware, TimingMiddleware, LoggingMiddleware
mcp.add_middleware(ErrorHandlingMiddleware()) # 第一个:捕获错误
mcp.add_middleware(TimingMiddleware()) # 第二个:计时请求
mcp.add_middleware(LoggingMiddleware(level="INFO"))
mcp.add_middleware(RateLimitingMiddleware(max_requests=100, window_seconds=60))
mcp.add_middleware(ResponseCachingMiddleware(ttl_seconds=300, storage=RedisStore()))自定义中间件:
python
from fastmcp.middleware import BaseMiddleware
class AccessControlMiddleware(BaseMiddleware):
async def on_call_tool(self, tool_name, arguments, context):
user = context.fastmcp_context.get_state("user_id")
if user not in self.allowed_users:
raise PermissionError(f"用户未授权")
return await self.next(tool_name, arguments, context)钩子层级: (所有请求)→ / → // → (列表操作)
on_messageon_requeston_notificationon_call_toolon_read_resourceon_get_prompton_list_*Server Composition
服务器组合
Two Strategies:
-
- Static snapshot: One-time copy at import, changes don't propagate, fast (no runtime delegation). Use for: Finalized component bundles.
import_server() -
- Dynamic link: Live runtime link, changes immediately visible, runtime delegation (slower). Use for: Modular runtime composition.
mount()
Basic Usage:
python
undefined两种策略:
-
- 静态快照:导入时一次性复制,后续变更不会同步,速度快(无运行时委托)。适用场景:已定型的组件包。
import_server() -
- 动态链接:运行时实时链接,变更立即生效,运行时委托(速度较慢)。适用场景:模块化运行时组合。
mount()
基本用法:
python
undefinedImport (static)
导入(静态)
main_server.import_server(api_server) # One-time copy
main_server.import_server(api_server) # 一次性复制
Mount (dynamic)
挂载(动态)
main_server.mount(api_server, prefix="api") # Tools: api.fetch_data
main_server.mount(db_server, prefix="db") # Resources: resource://db/path
**Tag Filtering:**
```python
@api_server.tool(tags=["public"])
def public_api(): pass
main_server.import_server(api_server, include_tags=["public"]) # Only public
main_server.mount(api_server, prefix="api", exclude_tags=["admin"]) # No adminResource Prefix Formats:
- Path (default since v2.4.0):
resource://prefix/path - Protocol (legacy):
prefix+resource://path
python
main_server.mount(subserver, prefix="api", resource_prefix_format="path")main_server.mount(api_server, prefix="api") # 工具路径:api.fetch_data
main_server.mount(db_server, prefix="db") # 资源路径:resource://db/path
**标签过滤:**
```python
@api_server.tool(tags=["public"])
def public_api(): pass
main_server.import_server(api_server, include_tags=["public"]) # 仅导入公共工具
main_server.mount(api_server, prefix="api", exclude_tags=["admin"]) # 排除管理员工具资源前缀格式:
- 路径格式(v2.4.0起默认):
resource://prefix/path - 协议格式(旧版):
prefix+resource://path
python
main_server.mount(subserver, prefix="api", resource_prefix_format="path")OAuth & Authentication
OAuth与身份认证
4 Authentication Patterns:
- Token Validation (): Validate external tokens
JWTVerifier - External Identity Providers (): OAuth 2.0/OIDC with DCR
RemoteAuthProvider - OAuth Proxy (): Bridge to providers without DCR (GitHub, Google, Azure, AWS, Discord, Facebook)
OAuthProxy - Full OAuth (): Complete authorization server
OAuthProvider
Pattern 1: Token Validation
python
from fastmcp.auth import JWTVerifier
auth = JWTVerifier(issuer="https://auth.example.com", audience="my-server",
public_key=os.getenv("JWT_PUBLIC_KEY"))
mcp = FastMCP("Server", auth=auth)Pattern 3: OAuth Proxy (Production)
python
from fastmcp.auth import OAuthProxy
from key_value.stores import RedisStore
from key_value.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
auth = OAuthProxy(
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(host=os.getenv("REDIS_HOST"), password=os.getenv("REDIS_PASSWORD")),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
),
upstream_authorization_endpoint="https://github.com/login/oauth/authorize",
upstream_token_endpoint="https://github.com/login/oauth/access_token",
upstream_client_id=os.getenv("GITHUB_CLIENT_ID"),
upstream_client_secret=os.getenv("GITHUB_CLIENT_SECRET"),
enable_consent_screen=True # CRITICAL: Prevents confused deputy attacks
)
mcp = FastMCP("GitHub Auth", auth=auth)OAuth Proxy Features: Token factory pattern (issues own JWTs), consent screens (prevents bypass), PKCE support, RFC 7662 token introspection
Supported Providers: GitHub, Google, Azure, AWS Cognito, Discord, Facebook, WorkOS, AuthKit, Descope, Scalekit, OCI (v2.13.1)
Supabase Provider (v2.14.2+):
python
from fastmcp.auth import SupabaseProvider
auth = SupabaseProvider(
auth_route="/custom-auth", # Custom auth route (new in v2.14.2)
# ... other config
)4种认证模式:
- 令牌验证():验证外部令牌
JWTVerifier - 外部身份提供商():支持DCR的OAuth 2.0/OIDC
RemoteAuthProvider - OAuth代理():桥接到不支持DCR的提供商(GitHub、Google、Azure、AWS、Discord、Facebook)
OAuthProxy - 完整OAuth():完整的授权服务器
OAuthProvider
模式1:令牌验证
python
from fastmcp.auth import JWTVerifier
auth = JWTVerifier(issuer="https://auth.example.com", audience="my-server",
public_key=os.getenv("JWT_PUBLIC_KEY"))
mcp = FastMCP("Server", auth=auth)模式3:OAuth代理(生产环境推荐)
python
from fastmcp.auth import OAuthProxy
from key_value.stores import RedisStore
from key_value.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
auth = OAuthProxy(
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(host=os.getenv("REDIS_HOST"), password=os.getenv("REDIS_PASSWORD")),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
),
upstream_authorization_endpoint="https://github.com/login/oauth/authorize",
upstream_token_endpoint="https://github.com/login/oauth/access_token",
upstream_client_id=os.getenv("GITHUB_CLIENT_ID"),
upstream_client_secret=os.getenv("GITHUB_CLIENT_SECRET"),
enable_consent_screen=True # 关键:防止混淆代理攻击
)
mcp = FastMCP("GitHub Auth", auth=auth)OAuth代理特性: 令牌工厂模式(签发自有JWT)、同意屏幕(防止绕过)、PKCE支持、RFC 7662令牌 introspection
支持的提供商: GitHub、Google、Azure、AWS Cognito、Discord、Facebook、WorkOS、AuthKit、Descope、Scalekit、OCI(v2.13.1)
Supabase提供商(v2.14.2+):
python
from fastmcp.auth import SupabaseProvider
auth = SupabaseProvider(
auth_route="/custom-auth", # 自定义认证路由(v2.14.2新增)
# ... 其他配置
)Icons, API Integration, Cloud Deployment
图标、API集成、云部署
Icons: Add to servers, tools, resources, prompts. Use , data URIs via or (v2.13.1).
Icon(url, size)Icon.from_file()Image.to_data_uri()API Integration (3 Patterns):
- Manual: with base_url/headers/timeout
httpx.AsyncClient - OpenAPI Auto-Gen: - GET→Resources/Templates, POST/PUT/DELETE→Tools
FastMCP.from_openapi(spec, client, route_maps) - FastAPI Conversion:
FastMCP.from_fastapi(app, httpx_client_kwargs)
Cloud Deployment Critical Requirements:
- ❗ Module-level server named ,
mcp, orserverapp - PyPI dependencies only in requirements.txt
- Public GitHub repo (or accessible)
- Environment variables for config
python
undefined图标: 可为服务器、工具、资源、提示词添加图标。使用,或通过或生成数据URI(v2.13.1)。
Icon(url, size)Icon.from_file()Image.to_data_uri()API集成(3种模式):
- 手动集成:使用配置base_url/headers/timeout
httpx.AsyncClient - OpenAPI自动生成:- GET→资源/模板,POST/PUT/DELETE→工具
FastMCP.from_openapi(spec, client, route_maps) - FastAPI转换:
FastMCP.from_fastapi(app, httpx_client_kwargs)
云部署关键要求:
- ❗ 模块级服务器,命名为、
mcp或serverapp - requirements.txt中仅包含PyPI依赖
- 公开的GitHub仓库(或可访问的仓库)
- 使用环境变量配置
python
undefined✅ CORRECT: Module-level export
✅ 正确方式:模块级导出
mcp = FastMCP("server") # At module level!
mcp = FastMCP("server") # 必须在模块级别定义!
❌ WRONG: Function-wrapped
❌ 错误方式:函数包裹
def create_server():
return FastMCP("server") # Too late for cloud!
**Deployment:** https://fastmcp.cloud → Sign in → Create Project → Select repo → Deploy
**Client Config (Claude Desktop):**
```json
{"mcpServers": {"my-server": {"url": "https://project.fastmcp.app/mcp", "transport": "http"}}}def create_server():
return FastMCP("server") # 云部署无法识别!
**部署流程:** https://fastmcp.cloud → 登录 → 创建项目 → 选择仓库 → 部署
**客户端配置(Claude桌面端):**
```json
{"mcpServers": {"my-server": {"url": "https://project.fastmcp.app/mcp", "transport": "http"}}}30 Common Errors (With Solutions)
30种常见错误及解决方案
Error 1: Missing Server Object
错误1:缺少服务器对象
Error:
Cause: Server not exported at module level (FastMCP Cloud requirement)
Solution: at module level, not inside functions
RuntimeError: No server object found at module levelmcp = FastMCP("server")错误信息:
原因: 服务器未在模块级别导出(FastMCP Cloud要求)
解决方案: 在模块级别定义,不要放在函数内部
RuntimeError: No server object found at module levelmcp = FastMCP("server")Error 2: Async/Await Confusion
错误2:Async/Await混淆
Error: ,
Cause: Mixing sync/async incorrectly
Solution: Use for tools with , sync for non-async code
RuntimeError: no running event loopTypeError: object coroutine can't be used in 'await'async defawaitdef错误信息: ,
原因: 错误地混合同步/异步代码
解决方案: 带的工具使用,非异步代码使用同步
RuntimeError: no running event loopTypeError: object coroutine can't be used in 'await'awaitasync defdefError 3: Context Not Injected
错误3:上下文未注入
Error:
Cause: Missing type annotation
Solution: - type hint required!
TypeError: missing 1 required positional argument: 'context'Contextasync def tool(context: Context)错误信息:
原因: 缺少类型注解
解决方案: 定义工具时使用 - 必须添加类型提示!
TypeError: missing 1 required positional argument: 'context'Contextasync def tool(context: Context)Error 4: Resource URI Syntax
错误4:资源URI语法错误
Error:
Cause: Resource URI missing scheme prefix
Solution: Use not
ValueError: Invalid resource URI: missing scheme@mcp.resource("data://config")@mcp.resource("config")错误信息:
原因: 资源URI缺少协议前缀
解决方案: 使用而非
ValueError: Invalid resource URI: missing scheme@mcp.resource("data://config")@mcp.resource("config")Error 5: Resource Template Parameter Mismatch
错误5:资源模板参数不匹配
Error:
Cause: Function parameter names don't match URI template
Solution: → - names must match exactly
TypeError: get_user() missing 1 required positional argument@mcp.resource("user://{user_id}/profile")def get_user(user_id: str)错误信息:
原因: 函数参数名称与URI模板不匹配
解决方案: → - 名称必须完全匹配
TypeError: get_user() missing 1 required positional argument@mcp.resource("user://{user_id}/profile")def get_user(user_id: str)Error 6: Pydantic Validation Error
错误6:Pydantic验证错误
Error:
Cause: Type hints don't match provided data
Solution: Use Pydantic models:
ValidationError: value is not a valid integerclass Params(BaseModel): query: str = Field(min_length=1)错误信息:
原因: 类型提示与提供的数据不匹配
解决方案: 使用Pydantic模型:
ValidationError: value is not a valid integerclass Params(BaseModel): query: str = Field(min_length=1)Error 7: Transport/Protocol Mismatch
错误7:传输/协议不匹配
Error:
Cause: Client and server using incompatible transports
Solution: Match transports - stdio: + , HTTP: +
ConnectionError: Server using different transportmcp.run(){"command": "python", "args": ["server.py"]}mcp.run(transport="http", port=8000){"url": "http://localhost:8000/mcp", "transport": "http"}HTTP Timeout Issue (Fixed in v2.14.3):
- HTTP transport was defaulting to 5-second timeout instead of MCP's 30-second default (GitHub Issue #2845)
- Tools taking >5 seconds would fail silently in v2.14.2 and earlier
- Solution: Upgrade to fastmcp>=2.14.3 (timeout now respects MCP's 30s default)
错误信息:
原因: 客户端与服务器使用不兼容的传输方式
解决方案: 保持传输方式一致 - stdio: + ,HTTP: +
ConnectionError: Server using different transportmcp.run(){"command": "python", "args": ["server.py"]}mcp.run(transport="http", port=8000){"url": "http://localhost:8000/mcp", "transport": "http"}HTTP超时问题(v2.14.3已修复):
- HTTP传输默认超时为5秒,而非MCP标准的30秒(GitHub Issue #2845)
- v2.14.2及更早版本中,耗时超过5秒的工具会静默失败
- 解决方案:升级到fastmcp>=2.14.3(现在超时时间遵循MCP的30秒默认值)
Error 8: Import Errors (Editable Package)
错误8:导入错误(可编辑包)
Error:
Cause: Package not properly installed
Solution: or use absolute imports or
ModuleNotFoundError: No module named 'my_package'pip install -e .export PYTHONPATH="/path/to/project"错误信息:
原因: 包未正确安装
解决方案: 使用,或使用绝对导入,或设置
ModuleNotFoundError: No module named 'my_package'pip install -e .export PYTHONPATH="/path/to/project"Error 9: Deprecation Warnings
错误9:弃用警告
Error:
Cause: Using old FastMCP v1 API
Solution: Use instead of
DeprecationWarning: 'mcp.settings' is deprecatedos.getenv("API_KEY")mcp.settings.get("API_KEY")错误信息:
原因: 使用旧版FastMCP v1 API
解决方案: 使用替代
DeprecationWarning: 'mcp.settings' is deprecatedos.getenv("API_KEY")mcp.settings.get("API_KEY")Error 10: Port Already in Use
错误10:端口已被占用
Error:
Cause: Port 8000 already occupied
Solution: Use different port or kill process
OSError: [Errno 48] Address already in use--port 8001lsof -ti:8000 | xargs kill -9错误信息:
原因: 端口8000已被占用
解决方案: 使用其他端口,或杀死占用进程
OSError: [Errno 48] Address already in use--port 8001lsof -ti:8000 | xargs kill -9Error 11: Schema Generation Failures
错误11:模式生成失败
Error:
Cause: Unsupported type hints (NumPy arrays, custom classes)
Solution: Return JSON-compatible types: or convert:
TypeError: Object of type 'ndarray' is not JSON serializablelist[float]{"values": np_array.tolist()}Custom Classes Not Supported (Community-sourced):
FastMCP supports all Pydantic-compatible types, but custom classes must be converted to dictionaries or Pydantic models for tool returns:
python
undefined错误信息:
原因: 使用了不支持的类型提示(NumPy数组、自定义类)
解决方案: 返回JSON兼容类型:,或转换为
TypeError: Object of type 'ndarray' is not JSON serializablelist[float]{"values": np_array.tolist()}自定义类不支持(社区发现):
FastMCP支持所有Pydantic兼容类型,但自定义类必须转换为字典或Pydantic模型才能作为工具返回值:
python
undefined❌ NOT SUPPORTED
❌ 不支持
class MyCustomClass:
def init(self, value: str):
self.value = value
@mcp.tool()
async def get_custom() -> MyCustomClass:
return MyCustomClass("test") # Serialization error
class MyCustomClass:
def init(self, value: str):
self.value = value
@mcp.tool()
async def get_custom() -> MyCustomClass:
return MyCustomClass("test") # 序列化错误
✅ SUPPORTED - Use dict or Pydantic
✅ 支持 - 使用dict或Pydantic
@mcp.tool()
async def get_custom() -> dict[str, str]:
obj = MyCustomClass("test")
return {"value": obj.value}
@mcp.tool()
async def get_custom() -> dict[str, str]:
obj = MyCustomClass("test")
return {"value": obj.value}
OR use Pydantic BaseModel
或使用Pydantic BaseModel
from pydantic import BaseModel
class MyModel(BaseModel):
value: str
@mcp.tool()
async def get_model() -> MyModel:
return MyModel(value="test") # Works!
**OutputSchema $ref Resolution (Fixed in v2.14.2)**:
- Root-level `$ref` in `outputSchema` wasn't being dereferenced ([GitHub Issue #2720](https://github.com/jlowin/fastmcp/issues/2720))
- Caused MCP spec non-compliance and client compatibility issues
- **Solution**: Upgrade to fastmcp>=2.14.2 (auto-dereferences $ref)from pydantic import BaseModel
class MyModel(BaseModel):
value: str
@mcp.tool()
async def get_model() -> MyModel:
return MyModel(value="test") # 正常工作!
**OutputSchema $ref解析(v2.14.2已修复):**
- `outputSchema`中的根级别`$ref`未被正确解析([GitHub Issue #2720](https://github.com/jlowin/fastmcp/issues/2720))
- 导致MCP规范不兼容和客户端兼容性问题
- **解决方案**:升级到fastmcp>=2.14.2(自动解析$ref)Error 12: JSON Serialization
错误12:JSON序列化错误
Error:
Cause: Returning non-JSON-serializable objects
Solution: Convert: , bytes:
TypeError: Object of type 'datetime' is not JSON serializabledatetime.now().isoformat().decode('utf-8')错误信息:
原因: 返回了非JSON可序列化的对象
解决方案: 转换为兼容格式:,字节数据:
TypeError: Object of type 'datetime' is not JSON serializabledatetime.now().isoformat().decode('utf-8')Error 13: Circular Import Errors
错误13:循环导入错误
Error:
Cause: Circular dependency (common in cloud deployment)
Solution: Use direct imports in : or lazy imports in functions
ImportError: cannot import name 'X' from partially initialized module__init__.pyfrom .api_client import APIClient错误信息:
原因: 循环依赖(云部署中常见)
解决方案: 在中使用直接导入:,或在函数中使用延迟导入
ImportError: cannot import name 'X' from partially initialized module__init__.pyfrom .api_client import APIClientError 14: Python Version Compatibility
错误14:Python版本兼容性
Error:
Cause: Using deprecated Python 3.12+ methods
Solution: Use instead of
DeprecationWarning: datetime.utcnow() is deprecateddatetime.now(timezone.utc)datetime.utcnow()错误信息:
原因: 使用了Python 3.12+中已弃用的方法
解决方案: 使用替代
DeprecationWarning: datetime.utcnow() is deprecateddatetime.now(timezone.utc)datetime.utcnow()Error 15: Import-Time Execution
错误15:导入时执行异步代码
Error:
Cause: Creating async resources at module import time
Solution: Use lazy initialization - create connection class with async method, call when needed in tools
RuntimeError: Event loop is closedconnect()错误信息:
原因: 在模块导入时创建异步资源
解决方案: 使用延迟初始化 - 创建带异步方法的连接类,在工具中需要时调用
RuntimeError: Event loop is closedconnect()Error 16: Storage Backend Not Configured
错误16:未配置存储后端
Error: ,
Cause: Using default memory storage in production without persistence
Solution: Use encrypted DiskStore (single instance) or RedisStore (multi-instance) with
RuntimeError: OAuth tokens lost on restartValueError: Cache not persistingFernetEncryptionWrapper错误信息: ,
原因: 生产环境中使用默认内存存储,无持久化
解决方案: 使用加密的DiskStore(单实例)或RedisStore(多实例),搭配
RuntimeError: OAuth tokens lost on restartValueError: Cache not persistingFernetEncryptionWrapperError 17: Lifespan Not Passed to ASGI App
错误17:未将生命周期传递给ASGI应用
Error: ,
Cause: FastMCP with FastAPI/Starlette without passing lifespan (v2.13.0 requirement)
Solution: - MUST pass lifespan!
RuntimeError: Database connection never initializedWarning: MCP lifespan hooks not runningapp = FastAPI(lifespan=mcp.lifespan)错误信息: ,
原因: FastMCP与FastAPI/Starlette集成时未传递生命周期(v2.13.0要求)
解决方案: - 必须传递lifespan!
RuntimeError: Database connection never initializedWarning: MCP lifespan hooks not runningapp = FastAPI(lifespan=mcp.lifespan)Error 18: Middleware Execution Order Error
错误18:中间件执行顺序错误
Error:
Cause: Incorrect middleware ordering (order matters!)
Solution: ErrorHandling → Timing → Logging → RateLimiting → ResponseCaching (this order)
RuntimeError: Rate limit not checked before caching错误信息:
原因: 中间件顺序错误(顺序至关重要!)
解决方案: 错误处理→计时→日志→限流→响应缓存(此顺序)
RuntimeError: Rate limit not checked before cachingError 19: Circular Middleware Dependencies
错误19:中间件循环依赖
Error:
Cause: Middleware not calling or calling incorrectly
Solution: Always call in middleware hooks
RecursionError: maximum recursion depth exceededself.next()result = await self.next(tool_name, arguments, context)错误信息:
原因: 中间件未调用或调用方式错误
解决方案: 在中间件钩子中始终调用
RecursionError: maximum recursion depth exceededself.next()result = await self.next(tool_name, arguments, context)Error 20: Import vs Mount Confusion
错误20:导入与挂载混淆
Error: ,
Cause: Using when was needed (or vice versa)
Solution: for static bundles (one-time copy), for dynamic composition (live link)
RuntimeError: Subserver changes not reflectedValueError: Unexpected tool namespacingimport_server()mount()import_server()mount()错误信息: ,
原因: 应该使用时使用了(反之亦然)
解决方案: 用于静态包(一次性复制),用于动态组合(实时链接)
RuntimeError: Subserver changes not reflectedValueError: Unexpected tool namespacingmount()import_server()import_server()mount()Error 21: Resource Prefix Format Mismatch
错误21:资源前缀格式不匹配
Error:
Cause: Using wrong resource prefix format
Solution: Path format (default v2.4.0+): , Protocol (legacy): - set with
ValueError: Resource not found: resource://api/usersresource://prefix/pathprefix+resource://pathresource_prefix_format="path"错误信息:
原因: 使用了错误的资源前缀格式
解决方案: 路径格式(v2.4.0+默认):,协议格式(旧版): - 可通过设置
ValueError: Resource not found: resource://api/usersresource://prefix/pathprefix+resource://pathresource_prefix_format="path"Error 22: OAuth Proxy Without Consent Screen
错误22:OAuth代理未启用同意屏幕
Error:
Cause: OAuth Proxy without consent screen (security vulnerability)
Solution: Always set - prevents confused deputy attacks (CRITICAL)
SecurityWarning: Authorization bypass possibleenable_consent_screen=True错误信息:
原因: OAuth代理未启用同意屏幕(安全漏洞)
解决方案: 始终设置 - 防止混淆代理攻击(至关重要)
SecurityWarning: Authorization bypass possibleenable_consent_screen=TrueError 23: Missing JWT Signing Key in Production
错误23:生产环境缺少JWT签名密钥
Error:
Cause: OAuth Proxy missing
Solution: Generate: , store in env var, pass to
ValueError: JWT signing key required for OAuth Proxyjwt_signing_keysecrets.token_urlsafe(32)FASTMCP_JWT_SIGNING_KEYOAuthProxy(jwt_signing_key=...)错误信息:
原因: OAuth代理缺少
解决方案: 生成密钥:,存储在环境变量中,传递给
ValueError: JWT signing key required for OAuth Proxyjwt_signing_keysecrets.token_urlsafe(32)FASTMCP_JWT_SIGNING_KEYOAuthProxy(jwt_signing_key=...)Error 24: Icon Data URI Format Error
错误24:图标数据URI格式错误
Error:
Cause: Incorrectly formatted data URI for icons
Solution: Use or (v2.13.1) - don't manually format
ValueError: Invalid data URI formatIcon.from_file("/path/icon.png", size="medium")Image.to_data_uri()错误信息:
原因: 图标数据URI格式不正确
解决方案: 使用或(v2.13.1) - 不要手动格式化
ValueError: Invalid data URI formatIcon.from_file("/path/icon.png", size="medium")Image.to_data_uri()Error 25: Lifespan Behavior Change (v2.13.0)
错误25:生命周期行为变更(v2.13.0)
Error:
Cause: Expecting v2.12 behavior (per-session) in v2.13.0+ (per-server)
Solution: v2.13.0+ lifespans run ONCE per server, not per session - use middleware for per-session logic
Warning: Lifespan runs per-server, not per-session错误信息:
原因: 在v2.13.0+中仍期望v2.12的行为(每个会话执行一次)
解决方案: v2.13.0+的生命周期每个服务器仅执行一次,会话级逻辑使用中间件实现
Warning: Lifespan runs per-server, not per-sessionError 26: BearerAuthProvider Removed (v2.14.0)
错误26:BearerAuthProvider已移除(v2.14.0)
Error:
Cause: module removed in v2.14.0
Solution: Use for token validation or for full OAuth flows:
ImportError: cannot import name 'BearerAuthProvider' from 'fastmcp.auth'BearerAuthProviderJWTVerifierOAuthProxypython
undefined错误信息:
原因: v2.14.0中移除了模块
解决方案: 使用进行令牌验证,或使用实现完整OAuth流程:
ImportError: cannot import name 'BearerAuthProvider' from 'fastmcp.auth'BearerAuthProviderJWTVerifierOAuthProxypython
undefinedBefore (v2.13.x)
之前的写法(v2.13.x)
from fastmcp.auth import BearerAuthProvider
from fastmcp.auth import BearerAuthProvider
After (v2.14.0+)
现在的写法(v2.14.0+)
from fastmcp.auth import JWTVerifier
auth = JWTVerifier(issuer="...", audience="...", public_key="...")
undefinedfrom fastmcp.auth import JWTVerifier
auth = JWTVerifier(issuer="...", audience="...", public_key="...")
undefinedError 27: Context.get_http_request() Removed (v2.14.0)
错误27:Context.get_http_request()已移除(v2.14.0)
Error:
Cause: method removed in v2.14.0
Solution: Access request info through middleware or use exposed to middleware
AttributeError: 'Context' object has no attribute 'get_http_request'Context.get_http_request()InitializeResult错误信息:
原因: v2.14.0中移除了方法
解决方案: 通过中间件访问请求信息,或使用中间件暴露的
AttributeError: 'Context' object has no attribute 'get_http_request'Context.get_http_request()InitializeResultError 28: Image Import Path Changed (v2.14.0)
错误28:Image导入路径变更(v2.14.0)
Error:
Cause: top-level import removed in v2.14.0
Solution: Use new import path:
ImportError: cannot import name 'Image' from 'fastmcp'fastmcp.Imagepython
undefined错误信息:
原因: v2.14.0中移除了顶层导入
解决方案: 使用新的导入路径:
ImportError: cannot import name 'Image' from 'fastmcp'fastmcp.Imagepython
undefinedBefore (v2.13.x)
之前的写法(v2.13.x)
from fastmcp import Image
from fastmcp import Image
After (v2.14.0+)
现在的写法(v2.14.0+)
from fastmcp.utilities import Image
undefinedfrom fastmcp.utilities import Image
undefinedError 29: FastAPI Mount Path Doubling
错误29:FastAPI挂载路径重复
Error: Client can't connect to endpoint, gets 404
Source: GitHub Issue #2961
Cause: Mounting FastMCP at creates endpoint at due to path prefix duplication
Solution: Mount at root or adjust client config
/mcp/mcp/mcp/mcp/python
undefined错误信息: 客户端无法连接到端点,返回404
来源: GitHub Issue #2961
原因: 将FastMCP挂载到时,由于路径前缀重复,实际端点为
解决方案: 挂载到根路径,或调整客户端配置
/mcp/mcp/mcp/mcp/python
undefined❌ WRONG - Creates /mcp/mcp endpoint
❌ 错误写法 - 生成/mcp/mcp端点
from fastapi import FastAPI
from fastmcp import FastMCP
mcp = FastMCP("server")
app = FastAPI(lifespan=mcp.lifespan)
app.mount("/mcp", mcp) # Endpoint becomes /mcp/mcp
from fastapi import FastAPI
from fastmcp import FastMCP
mcp = FastMCP("server")
app = FastAPI(lifespan=mcp.lifespan)
app.mount("/mcp", mcp) # 实际端点为/mcp/mcp
✅ CORRECT - Mount at root
✅ 正确写法 - 挂载到根路径
app.mount("/", mcp) # Endpoint is /mcp
app.mount("/", mcp) # 端点为/mcp
✅ OR adjust client config
✅ 或调整客户端配置
In claude_desktop_config.json:
在claude_desktop_config.json中:
{"url": "http://localhost:8000/mcp/mcp", "transport": "http"}
**Critical**: Must also pass `lifespan=mcp.lifespan` to FastAPI (see Error #17).{"url": "http://localhost:8000/mcp/mcp", "transport": "http"}
**关键提示**:必须同时将`lifespan=mcp.lifespan`传递给FastAPI(参考错误#17)。Error 30: Background Tasks Fail with "No Active Context" (ASGI Mount)
错误30:后台任务提示“无活动上下文”(ASGI挂载)
Error:
Source: GitHub Issue #2877
Cause: ContextVar propagation issue when FastMCP mounted in FastAPI/Starlette with background tasks ()
Solution: Upgrade to fastmcp>=2.14.3
RuntimeError: No active context foundtask=Truepython
undefined错误信息:
来源: GitHub Issue #2877
原因: 当FastMCP挂载到FastAPI/Starlette并使用后台任务()时,ContextVar传播出现问题
解决方案: 升级到fastmcp>=2.14.3
RuntimeError: No active context foundtask=Truepython
undefinedIn v2.14.2 and earlier - FAILS
v2.14.2及更早版本 - 失败
from fastapi import FastAPI
from fastmcp import FastMCP, Context
mcp = FastMCP("server")
app = FastAPI(lifespan=mcp.lifespan)
@mcp.tool(task=True)
async def sample(name: str, ctx: Context) -> dict:
# RuntimeError: No active context found
await ctx.report_progress(1, 1, "Processing")
return {"status": "OK"}
app.mount("/", mcp)
from fastapi import FastAPI
from fastmcp import FastMCP, Context
mcp = FastMCP("server")
app = FastAPI(lifespan=mcp.lifespan)
@mcp.tool(task=True)
async def sample(name: str, ctx: Context) -> dict:
# RuntimeError: No active context found
await ctx.report_progress(1, 1, "处理中")
return {"状态": "OK"}
app.mount("/", mcp)
✅ FIXED in v2.14.3
✅ v2.14.3已修复
pip install fastmcp>=2.14.3
pip install fastmcp>=2.14.3
**Note**: Related to Error #17 (Lifespan Not Passed to ASGI App).
---
**注意**:与错误#17(未将生命周期传递给ASGI应用)相关。
---Production Patterns, Testing, CLI
生产模式、测试、CLI
4 Production Patterns:
- Utils Module: Single with Config class, format_success/error helpers
utils.py - Connection Pooling: Singleton with
httpx.AsyncClientclass methodget_client() - Retry with Backoff:
retry_with_backoff(func, max_retries=3, initial_delay=1.0, exponential_base=2.0) - Time-Based Caching: with
TimeBasedCache(ttl=300)and.get()methods.set()
Testing:
- Unit: +
pytest+create_test_client(test_server)await client.call_tool() - Integration: +
Client("server.py")+list_tools()+call_tool()list_resources()
CLI Commands:
bash
fastmcp dev server.py # Run with inspector
fastmcp install server.py # Install to Claude Desktop
FASTMCP_LOG_LEVEL=DEBUG fastmcp dev # Debug loggingBest Practices: Factory pattern with module-level export, environment config with validation, comprehensive docstrings (LLMs read these!), health check resources
Project Structure:
- Simple: ,
server.py,requirements.txt,.envREADME.md - Production: (server.py, utils.py, tools/, resources/, prompts/),
src/,tests/pyproject.toml
4种生产模式:
- 工具模块:单个文件,包含Config类、format_success/error辅助函数
utils.py - 连接池:单例,带
httpx.AsyncClient类方法get_client() - 退避重试:
retry_with_backoff(func, max_retries=3, initial_delay=1.0, exponential_base=2.0) - 基于时间的缓存:,带
TimeBasedCache(ttl=300)和.get()方法.set()
测试:
- 单元测试:+
pytest+create_test_client(test_server)await client.call_tool() - 集成测试:+
Client("server.py")+list_tools()+call_tool()list_resources()
CLI命令:
bash
fastmcp dev server.py # 带检查器运行
fastmcp install server.py # 安装到Claude桌面端
FASTMCP_LOG_LEVEL=DEBUG fastmcp dev # 调试日志最佳实践: 带模块级导出的工厂模式、带验证的环境配置、全面的文档字符串(LLM会读取!)、健康检查资源
项目结构:
- 简单结构:、
server.py、requirements.txt、.envREADME.md - 生产结构:(server.py、utils.py、tools/、resources/、prompts/)、
src/、tests/pyproject.toml
References & Summary
参考资料与总结
Official: https://github.com/jlowin/fastmcp, https://fastmcp.cloud, https://modelcontextprotocol.io, Context7:
Related Skills: openai-api, claude-api, cloudflare-worker-base, typescript-mcp
Package Versions: fastmcp>=2.14.2 (PyPI), Python>=3.10 (3.13 supported in v2.14.1+), httpx, pydantic, py-key-value-aio, cryptography
Last Updated: 2026-01-21
/jlowin/fastmcp17 Key Takeaways:
- Module-level server export (FastMCP Cloud)
- Persistent storage (Disk/Redis) for OAuth/caching
- Server lifespans for resource management
- Middleware order: errors → timing → logging → rate limiting → caching
- Composition: (static) vs
import_server()(dynamic)mount() - OAuth security: consent screens + encrypted storage + JWT signing
- Async/await properly (don't block event loop)
- Structured error handling
- Avoid circular imports
- Test locally ()
fastmcp dev - Environment variables (never hardcode secrets)
- Comprehensive docstrings (LLMs read!)
- Production patterns (utils, pooling, retry, caching)
- OpenAPI auto-generation
- Health checks + monitoring
- Background tasks for long-running operations ()
task=True - Sampling with tools for agentic workflows ()
ctx.sample(tools=[...])
Production Readiness: Encrypted storage, 4 auth patterns, 8 middleware types, modular composition, OAuth security (consent screens, PKCE, RFC 7662), response caching, connection pooling, timing middleware, background tasks, agentic sampling, FastAPI/Starlette mounting, v3.0 provider architecture
Prevents 30+ errors. 90-95% token savings.
官方链接: https://github.com/jlowin/fastmcp, https://fastmcp.cloud, https://modelcontextprotocol.io, Context7:
相关技能: openai-api, claude-api, cloudflare-worker-base, typescript-mcp
依赖版本: fastmcp>=2.14.2(PyPI), Python>=3.10(v2.14.1+支持3.13), httpx, pydantic, py-key-value-aio, cryptography
最后更新: 2026-01-21
/jlowin/fastmcp17个关键要点:
- 模块级服务器导出(FastMCP Cloud要求)
- 持久化存储(磁盘/Redis)用于OAuth/缓存
- 服务器生命周期用于资源管理
- 中间件顺序:错误处理→计时→日志→限流→缓存
- 组合策略:(静态)vs
import_server()(动态)mount() - OAuth安全:同意屏幕+加密存储+JWT签名
- 正确使用Async/Await(不要阻塞事件循环)
- 结构化错误处理
- 避免循环导入
- 本地测试()
fastmcp dev - 使用环境变量(不要硬编码密钥)
- 全面的文档字符串(LLM会读取!)
- 生产模式(工具、连接池、重试、缓存)
- OpenAPI自动生成
- 健康检查+监控
- 后台任务用于长时间运行的操作()
task=True - 工具调用采样用于智能体工作流()
ctx.sample(tools=[...])
生产就绪特性: 加密存储、4种认证模式、8种中间件类型、模块化组合、OAuth安全(同意屏幕、PKCE、RFC 7662)、响应缓存、连接池、计时中间件、后台任务、智能体采样、FastAPI/Starlette挂载、v3.0提供器架构
可预防30+种错误,节省90-95%的令牌消耗。