mcp-agent OAuth 2.1 支持设计:从 Token 存储到委托授权流的完整实现指南 mcp-agent OAuth 2.1 支持设计从 Token 存储到委托授权流的完整实现指南【免费下载链接】mcp-agentBuild effective agents using Model Context Protocol and simple workflow patterns项目地址: https://gitcode.com/GitHub_Trending/mc/mcp-agent导读本文围绕 mcp-agent 仓库的 OAuth 支持设计文档docs/oauth_support_design.md展开系统梳理该项目如何基于 OAuth 2.1、PKCE、RFC 8414/9728/8707/9068 以及 MCP 委托授权 SEP为 保护 MCP Agent Cloud 服务器 与 向受保护的下游 MCP 服务器认证 两个方向提供完整闭环。读完本文你将掌握 mcp-agent 的 OAuth 模块架构src/mcp_agent/oauth/、Token 存储与刷新机制、auth/request委托授权流程、回调处理与回环兜底方案并能依据真实配置文件与示例examples/oauth/把 OAuth 集成到自己的 MCP 应用与工作流中。一、设计目标为什么要为 MCP Agent 引入 OAuth设计文档开篇给出了四个核心目标它们是理解整个模块的纲领保护 MCP Agent Cloud 服务器让 MCP 客户端通过标准 OAuth 2.1 流程获取 token再以 Bearer Token 访问受保护的 MCP 服务下游服务器认证让 MCP Agent 运行时能够向需要 OAuth 访问令牌的下游 MCP 服务器完成认证可插拔 Token 存储本地开发用内存存储多实例部署可切换 Redis仓库当前已同时落地两者规范兼容性对齐 MCP Authorization 规范RFC 8414 授权服务器元数据、RFC 9728 受保护资源元数据、OAuth 2.1 PKCE、Resource Indicators并兼容提案中的 delegated authorization SEP。从当前仓库源码看这套设计已基本落地为独立包 src/mcp_agent/oauth/并配套了四个测试文件tests/test_token_manager.py、tests/test_token_verifier.py、tests/test_oauth_utils.py、tests/test_audience_validation.py与三个可直接运行的示例examples/oauth/interactive_tool、examples/oauth/pre_authorize、examples/oauth/protected_by_oauth。二、架构总览六大组件与两类核心数据流2.1 组件清单设计文档规划的六大组件与当前源码一一对应组件职责源码位置Auth Server 集成为 FastMCP 实例配置AuthSettings并挂载自定义TokenVerifier调用鉴权服务src/mcp_agent/server/app_server.py、src/mcp_agent/server/token_verifier.py受保护资源元数据通过 FastMCP hooks 提供/.well-known/oauth-protected-resource供客户端发现授权服务器src/mcp_agent/oauth/metadata.pyfetch_resource_metadata访问令牌校验在每次入站 MCP 请求上强制 Bearer Token 校验并把认证用户写入请求上下文MCPAgentTokenVerifier FastMCPAuthSettingsOAuth Token 服务mcp_agent.oauth包TokenStore/TokenRecord、InMemoryTokenStore、Redis 实现、TokenManager、OAuthHttpxAuth、AuthorizationFlowCoordinatorsrc/mcp_agent/oauth/委托授权 UI 流网关/session relay 扩展服务器可向 MCP 客户端发送auth/request通过客户端回调 URL 或托管回调端点收取授权码app_server.py中/internal/oauth/callback/{flow_id}路由与auth/request转发配置面扩展Settings与 per-server 的MCPServerAuthSettings描述 scopes、首选授权服务器、redirect URI 及全局 token-store 配置src/mcp_agent/config.py设计文档在规划时把 Redis 实现标注为 optional for multi-instance / follow-up但从源码看 src/mcp_agent/oauth/store/redis.py 中的RedisTokenStore已经完整落地因此下文按实现现状讲解。2.2 关键数据流入站请求客户端携带 Bearer Token 发起请求MCPAgentTokenVerifier基于 RFC 9068JWT Profile语义进行校验必要时先从/.well-known/oauth-authorization-server发现 introspection endpoint校验通过后token 被解析为携带身份信息的MCPAccessToken见 src/mcp_agent/oauth/access_token.py进而构造OAuthUserIdentityprovider subject email claims见 src/mcp_agent/oauth/identity.py该身份被传播到 workflows/sessions 上下文中使下游 OAuth 流程能感知正在操作的用户。OAuthUserIdentity的cache_keyprovider:subject是 token 按用户隔离存储的关键TokenManager在获取 token 时按identity → resource → authorization_server → scopes逐级尝试候选身份显式传入身份、当前上下文身份、session 身份、预配置默认身份从而决定命中哪份缓存。2.3 关键数据流出站 HTTP下游 MCP 服务器ServerRegistry检测到auth.oauth配置将 HTTP transport 包装为OAuthHttpxAuth见 src/mcp_agent/oauth/http/auth.py它向TokenManager请求访问令牌TokenManager检查存储缺失或过期时由AuthorizationFlowCoordinator依次执行 RFC 9728 资源元数据发现、RFC 8414 授权服务器元数据发现、PKCE、委托浏览器流经 MCP 客户端或回环兜底、用授权码换 token、缓存结果当响应返回 401/无效 token 时OAuthHttpxAuth.async_auth_flow会先invalidate旧 token再重新走ensure_access_token拿到刷新后的 token复制原始请求体并自动重试一次。OAuthHttpxAuth实现了httpx.Auth其中requires_request_body True保证在重试时用request.copy()保留原始请求体这是 401 自动重试正确性的关键细节。2.4 关键数据流Token 存储Token 以(user_identity, resource, authorization_server)为维度存储并附带 metadatascopes、expiry、refresh token、provider claims存储键由 src/mcp_agent/oauth/store/base.py 中的TokenStoreKey定义额外包含scope_fingerprint对 scopes 去重排序后的确定性指纹避免相同资源但不同 scope 的 token 相互污染存储实现提供乐观并发保护TokenManager内部为每个TokenStoreKey维护独立的asyncio.Lock防止并发刷新风暴可插拔后端默认InMemoryTokenStorestore/in_memory.py多实例部署用RedisTokenStorestore/redis.py。三、模块规划mcp_agent.oauth包逐文件解读设计文档给出了模块规划图仓库中实际落地为src/mcp_agent/oauth/ __init__.py access_token.py # MCPAccessToken带身份/claims 的扩展 token 模型 callbacks.py # OAuthCallbackRegistry异步回调投递flow_id / state 双通道 errors.py # 自定义异常层级 flow.py # AuthorizationFlowCoordinator identity.py # OAuthUserIdentity manager.py # TokenManager metadata.py # RFC 8414 / RFC 9728 元数据发现 pkce.py # PKCE state 工具 records.py # TokenRecord store/base.py # TokenStore Protocol TokenStoreKey store/in_memory.py # 默认内存存储 store/redis.py # Redis 多实例存储 http/auth.py # OAuthHttpxAuth各模块要点如下records.pyTokenRecordpydantic BaseModel承载 access_token、refresh_token、scopes、expires_at、token_type、resource、authorization_server 等字段is_expired(leeway_seconds...)支持刷新余量判断with_tokens()用于刷新后原地更新令牌与获取时间。identity.pyOAuthUserIdentity为 frozen dataclassfrom_access_token()从 introspection 结果构造身份另有两个合成身份——DEFAULT_PRECONFIGURED_IDENTITY无用户/session 时使用标记token_source: synthetic与session_identity()按 session_id 生成确定性身份。errors.py异常层级为OAuthFlowError基类→AuthorizationDeclined、CallbackTimeoutError、TokenRefreshError、MissingUserIdentityError。pkce.pygenerate_code_verifier默认 64 字符强制 43~128 的 RFC 7636 范围、generate_code_challengeSHA-256 base64urlS256 方法、generate_state默认 32 字节 token_urlsafe。metadata.pyfetch_resource_metadataRFC 9728、fetch_authorization_server_metadataRFC 8414、select_authorization_server优先选择配置的首选服务器否则取第一个、normalize_resource对 http/https URL 做 host 小写、去尾斜杠、去 query/fragment 的规范化。3.1 配置面OAuth 相关 Settings 模型设计文档要求扩展Settings与 per-server 认证配置当前 src/mcp_agent/config.py 中已落地四个模型MCPOAuthClientSettings下游服务器认证关键字段字段默认值说明enabledFalse是否对该下游服务器启用 OAuthscopes[]授权时请求的 OAuth scopesresourceNoneRFC 8707 受保护资源标识进入 authorize/token 请求authorization_serverNone授权服务器基础 URL元数据由此 root 发现client_id/client_secretNone授权服务器注册的客户端凭据access_token/refresh_token/expires_at/token_typeNone预配置 token可绕过交互式流程redirect_uri_options[]允许的 redirect URI 列表流程从中选择extra_authorize_params/extra_token_params{}追加到 authorize/token 请求的额外参数require_pkceTrue是否强制 PKCEuse_internal_callbackTrue是否优先使用内部回调 URL否则走回环include_resource_parameterTrue是否在 authorize/token 请求中带resource参数OAuthTokenStoreSettings全局 token 持久化字段默认值说明backendmemorymemory或redisredis_urlNoneRedis 连接 URLredis_prefixmcp_agent:oauth_tokensRedis key 前缀refresh_leeway_seconds60到期前多少秒触发刷新OAuthSettings全局 OAuth 设置字段默认值说明token_storeOAuthTokenStoreSettings()跨下游服务器共享的存储配置flow_timeout_seconds300≥30授权回调等待上限callback_base_urlNone内部回调基础 URLuse_internal_callback为 true 时使用loopback_ports[33418, 33419, 33420]纯客户端场景的回环端口候选列表为空则禁用回环MCPServerAuthSettings作为 per-server 的auth节点包含api_key与oauth: MCPOAuthClientSettings | None。3.2 服务器侧集成触点设计文档列出的集成触点均已落地src/mcp_agent/config.pyOAuth Settings 模型见上src/mcp_agent/core/context.py承载token_manager、token_store、oauth_config等上下文字段src/mcp_agent/app.py依据设置初始化 token store/managersrc/mcp_agent/server/app_server.pycreate_mcp_server_for_app()在启用授权时构造AuthSettings(issuer_url..., resource_server_url..., required_scopes...)并实例化MCPAgentTokenVerifier同时注册内部回调路由/internal/oauth/callback/{flow_id}GET/POSTinclude_in_schemaFalse通过callback_registry.deliver(flow_id, payload)投递授权结果relay 层在method auth/request时把请求转发给上游 session对应行 1084src/mcp_agent/server/token_verifier.pyMCPAgentTokenVerifier负责 introspection endpoint 的 well-known 发现/.well-known/oauth-authorization-server、token 校验与缓存、RFC 9068 audience 校验src/mcp_agent/mcp/ 的mcp_server_registry.py与mcp_connection_manager.py将OAuthHttpxAuth接入 HTTP transport并提供手动 teardown 辅助。四、OAuth 流程细节从发现到刷新设计文档将流程拆为五步以下结合源码逐层展开。4.1 发现Discovery若下游服务器响应 401 并带WWW-Authenticate解析其中的resource_metadataGET 资源元数据进而确定授权服务器 URL获取 RFC 8414 授权服务器元数据在配置且受支持时执行可选的动态客户端注册。TokenManager._resolve_oauth_context()manager.py实现了这一链路resource 默认取oauth_config.resource否则回退到服务器url经normalize_resource规范化资源元数据发现会依次尝试/.well-known/oauth-protected-resource/{path}与/.well-known/oauth-protected-resource两个候选 URL授权服务器选择遵循select_authorization_server(resource_metadata, preferred)优先匹配oauth_config.authorization_server未命中则告警并回退第一个授权服务器元数据同样尝试/.well-known/oauth-authorization-server/{path}与/.well-known/oauth-authorization-server两类元数据都有 300 秒5 分钟的进程内缓存避免每次请求都重复发现。4.2 授权请求Authorization RequestAuthorizationFlowCoordinator.authorize()flow.py按顺序校验user无身份抛出MissingUserIdentityError与client_id缺失抛出OAuthFlowError组装 redirect 候选优先use_internal_callback生成的内部回调{callback_base_url}/internal/oauth/callback/{flow_id}其次追加回环候选http://127.0.0.1:{port}/callback与http://localhost:{port}/callback端口取loopback_ports默认 33418/33419/33420再合并redirect_uri_options生成 PKCE verifier/challengeS256与安全随机state构造授权 URL参数含response_typecode、client_id、redirect_uri、scope、state、code_challenge、code_challenge_methodS256并按include_resource_parameter追加 RFC 8707 的resource参数最后并入extra_authorize_params把完整的request_payload含 url、message、redirect_uri_options、flow_id、scopes、timeout、token_endpoint、code_verifier、client_id、client_secret、issuer 等通过_send_auth_request以auth/request发送给上游 MCP 客户端若上游 session 不可用抛出AuthorizationDeclined则降级执行_run_loopback_flow见下节。4.3 回调处理Callback Handling首选路径MCP 客户端通过请求结果返回回调 URL payload协调器用_parse_callback_params解析 URL 的 query 与 fragment提取code与error回退路径 A内部托管回调授权服务器重定向到/internal/oauth/callback/{flow_id}该路由app_server.py同时支持 GET/POST、query 参数与 JSON/form body通过callback_registry把结果投递给等待中的 future回退路径 B原生应用风格回环_run_loopback_flow在 127.0.0.1 上依序尝试绑定候选端口成功后在本地启动asyncio.start_server临时 HTTP 监听器用选定的 redirect_uri 重写授权 URL打开浏览器同时把完整 URL 打印到终端供手动访问收到含code或error的回调后以state或flow_id路由投递。回调结果统一进入校验先检查error再校验state与发起时的一致性防 CSRF最后提取code。callback_registrycallbacks.py是这套异步投递机制的核心create_handle/deliver/deliver_by_state/fail/discard以asyncio.Future为单元支持按 flow_id 与按 state 双通道投递并在超时/异常时兜底清理。4.4 Token 交换与存储Token Exchange / StoragePOST token endpoint携带grant_typeauthorization_code、code、redirect_uri、client_id、code_verifier可选scope、resourceRFC 8707与extra_token_paramsconfidential client 追加client_secret解析响应access_token、refresh_token、expires_in换算为绝对时间戳、scope以响应为准回退到请求 scope、token_type组装TokenRecord并持久化TokenManager.ensure_access_token在拿到新 token 后回填resource与authorization_server并写入 store。另有两种免交互的 token 注入路径预配置 tokenstore_preconfigured_token当配置中直接给出access_token时以DEFAULT_PRECONFIGURED_IDENTITY为身份写入 store完全绕过交互流程工作流预授权store_user_token通过workflows-store-credentials工具在异步工作流执行前缓存 token详见 examples/oauth/pre_authorize/README.md并校验调用方提供的authorization_server与解析出的 issuer 是否一致、scope 是否覆盖所需范围。4.5 刷新与吊销Refresh / Revocation刷新TokenManager.get_access_token_if_present/ensure_access_token在record.is_expired(leeway_secondsrefresh_leeway_seconds)默认 60 秒时自动尝试_refresh_tokenPOSTgrant_typerefresh_token携带 refresh_token、client_id、resource、scope可选与 client_secret成功后以响应中的新 access_token/refresh_token/expires_in 生成新TokenRecord回写 store失效刷新失败TokenRefreshError或 401 重试时调用invalidate()删除对应 key对非默认身份还会同步清理该身份下的默认身份副本吊销设计文档要求在授权服务器支持时提供吊销方法当前通过invalidate完成本地失效服务端吊销接口取决于授权服务器能力见开放问题。OAuthHttpxAuth将刷新与重试串成闭环发送请求 → 401 → invalidate 旧 token → 重新ensure_access_token→request.copy()保留 body → 以新 token 重发。五、并发安全避免刷新风暴设计文档与实现都强调了并发刷新控制。在TokenManager中每个TokenStoreKey对应一把asyncio.Lockself._locksdefaultdict(asyncio.Lock)任何 get/refresh/authorize 都在锁内进行ensure_access_token在等待锁之后会双重检查double-checkstore避免多个协程排队后重复发起授权流程InMemoryTokenStore内部另有asyncio.Lock保护字典读写。tests/test_token_manager.py中test_preconfigured_token_lookup_and_invalidation等用例直接验证了 store 的写入、命中与失效行为设计文档规划的 token store concurrency expiry handling 单元测试 对应tests/test_oauth_utils.py、tests/test_token_verifier.py、tests/test_audience_validation.py等测试套件。六、实战示例与配置examples/oauth/提供了三种互补场景总览见 examples/oauth/README.md。6.1 interactive_tool同步工具的完整授权码流场景MCP 服务器暴露github_org_search工具首次调用时向客户端发送auth/request客户端引导用户在浏览器完成 GitHub 登录后续调用复用已缓存 token无需再次弹窗。前提详见 examples/oauth/interactive_tool/README.md在 GitHub 创建 OAuth AppAuthorization callback URL 必须精确填写http://127.0.0.1:33418/callback与示例固定的回环端口一致由于 GitHub 不接受 RFC 8707resource参数示例配置中需关闭include_resource_parameter导出GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET安装依赖pip install -e .。运行# 终端一启动服务器 python examples/oauth/interactive_tool/server.py # 终端二运行客户端 python examples/oauth/interactive_tool/client.py客户端展示授权提示 → 浏览器批准 → GitHub 重定向回本地回调处理器 → 工具结果打印在客户端终端。服务器与客户端使用稳定 session ID首次授权后 token 被缓存并可跨运行复用。6.2 pre_authorize异步工作流的预授权场景Temporal 等后台执行的工作流无法自行交互式认证因此在工作流运行前通过workflows-store-credentials工具播种 token见 examples/oauth/pre_authorize/README.md。流程复制 secrets 模板并填入 GitHub OAuth client_id/client_secret导出已有 GitHub tokenexport GITHUB_ACCESS_TOKENgithub_pat_xxx启动服务器python examples/oauth/pre_authorize/main.py另一终端运行python examples/oauth/pre_authorize/client.py客户端先调用workflows-store-credentials缓存 token再调用github_org_search工作流后者通过gen_client(github, ...)访问 GitHub MCP 服务器。关键配置片段examples/oauth/pre_authorize/mcp_agent.config.yamloauth: loopback_ports: [33418, 33419, 33420] mcp: servers: github: transport: streamable_http url: https://api.githubcopilot.com/mcp/ auth: oauth: enabled: true scopes: [read:org, public_repo, user:email] authorization_server: https://github.com/login/oauth use_internal_callback: false include_resource_parameter: falsemain.py还展示了如何通过环境变量OAUTH_REDIS_URL切换到 Redis token store并动态改写settings.oauth.token_store。6.3 使用 Redis 持久化 token默认 token 存内存进程重启即丢失。改用 Redis 的完整步骤# 1. 启动 Redis docker run --rm -p 6379:6379 redis:7-alpine # 2. 安装可选依赖 pip install -e .[redis] # 3. 导出连接串 export OAUTH_REDIS_URLredis://127.0.0.1:6379设置环境变量后示例会自动切换到 Rediskey 前缀默认mcp_agent:oauth_tokens服务器重启后 token 仍可复用。若代码中未设OAUTH_REDIS_URL也可在配置中显式声明oauth: token_store: backend: redis redis_url: redis://127.0.0.1:6379 redis_prefix: mcp_agent:oauth_tokens refresh_leeway_seconds: 60RedisTokenStore用 URL-quote 后的user_key / resource / authorization_server / scope_fingerprint拼装 Redis key以 JSON 序列化TokenRecordredis包未安装时会提示pip install mcp-agent[redis]。七、测试策略设计文档规划的测试策略在仓库中已落地token store 并发与过期处理tests/test_token_manager.py预配置 token 存取与失效、用户 token 的工作流/session 元数据、身份去重等元数据发现 PKCE 生成纯 Python 测试tests/test_oauth_utils.py覆盖_candidate_resource_metadata_urls/_candidate_authorization_metadata_urls等候选 URL 构造逻辑tests/test_token_manager.py亦直接导入这些函数服务器 401 强制 WWW-Authenticatetests/test_token_verifier.py与tests/test_audience_validation.py验证 token 校验、introspection 发现及 RFC 9068 audience 校验委托授权流的端到端验证mocked HTTP fake MCP client 确保auth/request管线贯通由上述用例与示例配套覆盖。八、开放问题与后续方向设计文档明确列出的 follow-ups当前仍待推进运维加固token 轮换策略、速率限制MCPAgentTokenVerifier 定稿LastMile 授权服务器如何暴露 token introspection JWKS需要具体 endpoint 规格才能定稿校验器实现auth/requestSEP 的客户端采纳度需要能力检测capability detection在客户端广泛支持之前依赖托管回调回退与手动指引访问控制 DSL按 email/domain 的 include/exclude 规则待 token 身份 payload 定稿后评估。总结mcp-agent 的 OAuth 支持是一套完整覆盖保护自建 MCP 服务器与访问受保护下游 MCP 服务器双向场景的实现TokenRecord承载令牌TokenStore抽象出可插拔的内存/Redis 持久化TokenManager统一负责获取、刷新、失效与并发控制AuthorizationFlowCoordinator打通了auth/request委托流、内部托管回调与 127.0.0.1 回环兜底OAuthHttpxAuth则让下游 HTTP transport 获得自动取 token、401 自动刷新重试的能力。配合 examples/oauth/ 的交互式、预授权与 Redis 三组示例开发者可以快速把 OAuth 2.1 PKCE 的完整能力接入自己的 MCP 应用、异步工作流与多实例部署。【免费下载链接】mcp-agentBuild effective agents using Model Context Protocol and simple workflow patterns项目地址: https://gitcode.com/GitHub_Trending/mc/mcp-agent创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考