FastMCP 3.2.0 的 from_openapi() 如何携带用户的 OAuth2 令牌?
通过自定义 httpx.Auth,可以在每次调用 REST API 时读取 get_access_token().token,设置 Authorization: Bearer ... 请求头,再把配置了该认证方式的 httpx.AsyncClient 传给 FastMCP.from_openapi(client=...)。这样无需配置静态用户令牌,也不用逐个修改自动生成的工具。
为什么 MCP 认证成功后,API 请求仍然没有令牌?
原代码中的 auth=auth 用于认证访问 MCP server 的用户。自动生成的工具调用 REST API 时会发起另一条 HTTP 请求,需要在出站 HTTP client 上配置认证。
FastMCP 3.2.0 的 from_openapi() 将 client 传给 OpenAPI provider,而将 auth 等设置传给 MCP server。因此,只配置 OIDCProxy 不会自动为出站请求添加 Authorization 请求头。FastMCP 3.2.0 的 from_openapi() 实现
自定义工具 get_user_info() 能调用 get_access_token(),说明该工具可以访问当前请求的认证上下文。读取令牌本身不会修改其他 HTTP 请求。
为出站请求动态设置 Bearer token
保留原来的 JWTVerifier 和 OIDCProxy 配置。在 auth = OIDCProxy(...) 之后,用下面的代码替换加载 OpenAPI 文档和创建 mcp 的部分,并将示例中的 API 地址换成实际地址。
from contextlib import asynccontextmanager
import httpx
from fastmcp import FastMCP
from fastmcp.exceptions import ToolError
from fastmcp.server.dependencies import get_access_token
class UserBearerAuth(httpx.Auth):
def auth_flow(self, request):
# 每次发送请求时读取当前用户的令牌。
token = get_access_token()
if token is None or not token.token:
raise ToolError("当前请求没有可用的用户 access token")
request.headers["Authorization"] = f"Bearer {token.token}"
yield request
api_client = httpx.AsyncClient(
base_url="https://api.example.com",
auth=UserBearerAuth(),
follow_redirects=False,
)
@asynccontextmanager
async def lifespan(server: FastMCP):
try:
yield {}
finally:
await api_client.aclose()
spec_response = httpx.get("https://api.example.com/swagger.json")
spec_response.raise_for_status()
openapi_spec = spec_response.json()
mcp = FastMCP.from_openapi(
openapi_spec=openapi_spec,
client=api_client,
name="My API Server",
auth=auth,
lifespan=lifespan,
)
后面可以继续保留原来的 get_user_info() 工具和 mcp.run(...) 启动代码。注意补上原始粘贴代码中缺失的 Python 缩进。
这里的 auth_flow() 只读取请求上下文并修改请求头,不执行额外 I/O,可以用于 httpx.AsyncClient。HTTPX 支持通过继承 httpx.Auth 实现自定义认证。HTTPX 自定义认证文档
必须在发送请求时调用 get_access_token()。如果在服务器启动或创建 client 时读取,此时没有当前调用用户,无法取得对应的令牌。
OIDCProxy 下拿到的是哪种令牌?
需要区分 MCP 客户端提交的令牌和 IdP 签发的上游 access token。
FastMCP 3.2.0 的 OAuthProxy 会验证 FastMCP 签发的令牌,通过服务端映射找到上游令牌,再返回上游令牌的验证结果。OIDCProxy 继承了这套机制。因此,在题述配置中,可以从 get_access_token() 返回对象的 .token 字段取得上游 access token,无需直接复制入站 HTTP 请求的 Authorization 头。FastMCP 3.2.0 的 OAuthProxy 令牌处理实现
get_access_token() 返回的是令牌对象,实际用于 Bearer 认证的是 token.token;token.claims 用来读取声明信息。FastMCP 3.2.0 的请求依赖实现
接入后应检查什么?
- 检查令牌是否适用于目标 API。MCP 登录成功不代表该用户令牌具备 REST API 所需的 audience、scope 或角色。如果请求已携带令牌仍被拒绝,需要结合 API 的鉴权响应核对这些条件,不能仅凭状态码确定原因。
- 不要修改共享 client 的默认用户请求头。避免在工具中执行
api_client.headers["Authorization"] = ...,并发用户可能相互覆盖。示例只修改本次request.headers,认证对象也不保存用户令牌。 - 确认请求地址可信。
base_url应对应实际 REST API,并核对 OpenAPI 中的服务器地址和路径。该 client 专门用于目标 API;如果接口返回重定向,应先核实目标地址。 - 缺少令牌时停止调用。示例会直接报错,避免无意间以匿名身份请求受保护的 API。
验证时,用已认证的 MCP 客户端调用一个自动生成的工具,在受控环境中确认 API 收到了 Bearer 请求头,并核对用户身份。再分别用两个不同用户调用,检查是否保持各自的权限。日志只记录必要的鉴权结果,不输出完整令牌。
示例中的 OpenAPI 文档与原代码一样,仍在服务器启动时加载。如果 swagger.json 本身也需要用户认证,就需要另行设计文档加载方式;启动阶段无法依赖尚未发生的用户工具调用获取令牌。
备注:内容仅供参考。
