本地跑得好好的 MCP Server,一部署到服务器就懵了:stdio transport 根本没法远程访问,HTTP 模式直接裸奔,工具调用一超时就断连。这些坑,FastMCP 2.14 都给你填好了。
stdio 够用吗?不够
FastMCP 默认用 stdio transport------进程间通过标准输入输出通信。本地开发很香,Claude Desktop 直接配个启动命令就能用:
json
{
"mcpServers": {
"calculator": {
"command": "python",
"args": ["/path/to/server.py"]
}
}
}
但一上生产就卡了:stdio 只能单机单进程,远程客户端接不进来,没法做负载均衡,日志全混在标准输出里。你需要 HTTP transport。
HTTP Transport 基础版
FastMCP 2.0 开始支持 HTTP,把 mcp.run() 改成 mcp.run(transport="http", port=8000) 就行:
python
from fastmcp import FastMCP
mcp = FastMCP("Calculator")
@mcp.tool
def add(a: int, b: int) -> int:
return a + b
if __name__ == "__main__":
mcp.run(transport="http", port=8000, host="0.0.0.0")
客户端连 http://your-server:8000/mcp 就能调工具了。但这是裸奔------任何人都能调你的 API。
生产第一课:加鉴权
FastMCP 2.11 引入了完整的 OAuth 2.1 支持。最简单的方案是 JWT 验证:你的网关或认证服务签发 JWT,MCP Server 只验证不签发。
python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier
# 配置 JWT 验证器(对接你的 IdP 的 JWKS endpoint)
verifier = JWTVerifier(
jwks_url="https://your-idp.com/.well-known/jwks.json",
issuer="https://your-idp.com",
audience="your-mcp-server"
)
mcp = FastMCP("SecureCalculator", auth=verifier)
@mcp.tool
def add(a: int, b: int) -> int:
return a + b
if __name__ == "__main__":
mcp.run(transport="http", port=8000, host="0.0.0.0")
客户端请求时带上 Authorization: Bearer <token> 头,FastMCP 自动验证签名、过期时间、audience。验证失败直接返回 401,工具不会执行。
踩坑1:JWKS 缓存失效
JWTVerifier 默认缓存 JWKS 公钥 5 分钟。如果你的 IdP 频繁轮换密钥,会出现短暂的验证失败窗口。解决办法:
python
verifier = JWTVerifier(
jwks_url="https://your-idp.com/.well-known/jwks.json",
issuer="https://your-idp.com",
audience="your-mcp-server",
cache_maxsize=128, # 增加缓存容量
cache_ttl=300 # 5分钟TTL(可调)
)
生产环境建议开启密钥轮换的 grace period,新旧密钥共存一段时间。
Streamable HTTP:告别 SSE 的断连噩梦
FastMCP 2.14 支持了 MCP 规范新增的 Streamable HTTP transport。之前的 SSE (Server-Sent Events) transport 有个硬伤:长时间工具调用(比如跑个 5 分钟的数据分析)容易被中间代理(nginx、CDN)超时断连。
Streamable HTTP 用双向流 + chunked encoding 解决了这个问题:
python
from fastmcp import FastMCP
mcp = FastMCP("DataAnalyzer")
@mcp.tool
async def analyze_large_dataset(file_path: str) -> str:
# 模拟长时间运行的任务
import asyncio
await asyncio.sleep(300) # 5分钟
return "Analysis complete"
if __name__ == "__main__":
# 同时支持 SSE 和 Streamable HTTP,向后兼容
mcp.run(transport="http", port=8000)
FastMCP 的 HTTP transport 现在同时处理 SSE 和 Streamable HTTP 请求,客户端根据自己的能力选协议。老客户端继续用 SSE,新客户端用 Streamable HTTP。
踩坑2:Nginx 代理配置
Streamable HTTP 要求代理不能缓冲响应体,否则又回到超时断连的老路。Nginx 配置:
nginx
location /mcp {
proxy_pass http://localhost:8000;
proxy_buffering off; # 关键:禁用缓冲
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding on;
}
Background Tasks:工具调用不再阻塞
FastMCP 2.14 引入了 MCP 规范的后台任务支持。之前如果工具执行时间长,客户端只能傻等;现在可以让工具在后台跑,客户端轮询进度或等通知。
python
from fastmcp import FastMCP
mcp = FastMCP("AsyncCalculator")
@mcp.tool(task=True) # 标记为后台任务
async def compute_fibonacci(n: int) -> int:
"""计算第 n 个斐波那契数(假装很慢)"""
import asyncio
await asyncio.sleep(10)
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
if __name__ == "__main__":
mcp.run(transport="http", port=8000)
客户端调用 compute_fibonacci 时,FastMCP 立即返回一个 task_id,任务在后台执行。客户端可以:
- 轮询任务状态(
GET /mcp/tasks/{task_id}) - 等待任务完成并获取结果
默认用内存队列(重启丢失),生产环境建议用 Redis:
python
from fastmcp import FastMCP
from docket.brokers import RedisBroker
broker = RedisBroker("redis://localhost:6379/0")
mcp = FastMCP("AsyncCalculator", task_broker=broker)
# 其余代码不变
Redis broker 支持水平扩展:多个 FastMCP 实例共享任务队列,任何实例都能处理任务。
踩坑3:任务清理
后台任务完成后会保留在队列里,时间长了内存爆炸。FastMCP 2.14.5 修了内存泄漏,但你还需要配置任务过期:
python
from docket.brokers import MemoryBroker
broker = MemoryBroker(
result_ttl=3600 # 任务结果保留1小时后删除
)
mcp = FastMCP("AsyncCalculator", task_broker=broker)
Redis broker 自带 TTL,不用额外配置。
生产清单
把这些坑踩一遍,总结出生产部署检查清单:
- 网络绑定 :
host="0.0.0.0"允许外部访问,但必须配鉴权 - 鉴权方式:JWT 验证器(对接 IdP)或 OAuth 提供商(WorkOS、Auth0)
- Transport:用 HTTP,自动支持 SSE + Streamable HTTP
- 代理配置:Nginx 禁用 buffering,支持 chunked encoding
- 后台任务 :长时间工具标记
task=True,生产用 Redis broker - 监控日志:FastMCP 输出结构化日志,接入 ELK/Grafana
- 错误处理:工具函数抛异常会返回错误消息,不会让服务崩溃
代码全景
最后贴个生产级配置的完整示例:
python
import asyncio
from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier
from docket.brokers import RedisBroker
# JWT 鉴权
verifier = JWTVerifier(
jwks_url="https://your-idp.com/.well-known/jwks.json",
issuer="https://your-idp.com",
audience="calculator-mcp"
)
# Redis 后台任务队列
broker = RedisBroker("redis://localhost:6379/0")
mcp = FastMCP("ProductionCalculator", auth=verifier, task_broker=broker)
@mcp.tool
def add(a: int, b: int) -> int:
"""快速工具:同步返回"""
return a + b
@mcp.tool(task=True)
async def heavy_compute(n: int) -> int:
"""慢速工具:后台执行"""
await asyncio.sleep(30)
return n * n
if __name__ == "__main__":
mcp.run(transport="http", port=8000, host="0.0.0.0")
配套 Nginx:
nginx
upstream mcp_backend {
server 127.0.0.1:8000;
server 127.0.0.1:8001; # 水平扩展
}
server {
listen 443 ssl;
server_name mcp.example.com;
location /mcp {
proxy_pass http://mcp_backend;
proxy_buffering off;
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding on;
}
}
从本地 stdio 到生产 HTTP,FastMCP 的路径很清晰:先让工具跑起来,再加鉴权,最后上后台任务。每个特性都解决实际痛点,不是堆概念。
MCP Server 不是玩具,生产部署要考虑安全、可靠、可扩展。FastMCP 2.14 把这些都做成了开箱即用的功能,剩下的就是你的业务逻辑了。