PAYATHON 2026

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 本身也需要用户认证,就需要另行设计文档加载方式;启动阶段无法依赖尚未发生的用户工具调用获取令牌。

备注:内容仅供参考。