mcp-agent OAuth 预授权实战:用 `workflows-store-credentials` 为异步工作流预置凭据
2026/9/16 20:53:15 网站建设 项目流程

mcp-agent OAuth 预授权实战:用workflows-store-credentials为异步工作流预置凭据

【免费下载链接】mcp-agentBuild effective agents using Model Context Protocol and simple workflow patterns项目地址: https://gitcode.com/GitHub_Trending/mc/mcp-agent

mcp-agent 的examples/oauth/pre_authorize示例展示了一种关键的 OAuth 集成模式:在异步(Temporal)工作流真正运行之前,由客户端通过workflows-store-credentials工具把访问令牌缓存进 token store,使后续的工作流在执行时无需任何用户交互即可访问受保护的下游 MCP 服务器。本文以该示例为骨架,结合仓库中服务端工具、OAuth token manager 与配置模型的源码实现,完整讲解预授权模式的配置、运行步骤与底层原理,读完你可以在自己的项目里复刻这套「先种凭据、后跑后台工作流」的认证方案。

为什么需要「预授权」:后台工作流的认证困境

常规的 OAuth 授权流程(如 examples/oauth/interactive_tool 所演示的)需要在授权回调时弹出浏览器、等待用户确认。但对于部署在 Temporal worker 上的异步、长时间运行的工作流(例如examples/oauth/pre_authorize使用的execution_engine: temporal),执行环境往往:

  • 没有交互式终端,无法唤起浏览器;
  • 在独立进程中运行,用户不可能在工作流执行中途点击授权按钮。

解决思路就是预授权(pre-authorization):在正式运行工作流之前,先由一个具备用户上下文的客户端把 OAuth 令牌「种」进共享的 token store(内存或 Redis),工作流启动后从 store 中读取缓存的令牌,直接访问下游服务器。server-authentication.mdx 将这一模式概括为三步:带外获取令牌 → 调用workflows-store-credentials存入令牌 → 运行工作流读取缓存令牌,全程不再弹出任何授权提示。

示例整体架构

本示例共包含四个文件,各自职责如下:

文件角色
main.py基于 FastMCP 的工作流服务器,注册github_org_search工具与 Temporal 工作流任务,以 SSE 方式对外提供服务
client.py客户端:先调用workflows-store-credentials预置令牌,再调用github_org_search工作流
worker.pyTemporal worker,负责真正执行工作流与活动(activity)
mcp_agent.config.yaml服务端配置:Temporal 执行引擎、OAuth 参数、GitHub MCP 服务器声明
mcp_agent.secrets.yaml.example密钥模板,需要复制为mcp_agent.secrets.yaml并填入真实凭据

数据流大致为:client.py→ SSE 连接main.py启动的pre_authorize_server→ 调用workflows-store-credentials把 GitHub 令牌写入 token store → 调用github_org_search工具 → 该工具经app.executorgithub_org_search_activity调度到 Temporal → 活动通过gen_client连接 GitHub MCP 服务器,使用已缓存的令牌查询仓库。

前置条件与运行准备

1. 配置 GitHub OAuth 客户端凭据

先复制密钥模板并填充你的 GitHub OAuth App 凭据:

cp examples/oauth/pre_authorize/mcp_agent.secrets.yaml.example examples/oauth/pre_authorize/mcp_agent.secrets.yaml

编辑复制出来的文件,使其包含 OAuth App 的client_idclient_secret(mcp_agent.secrets.yaml.example):

mcp: servers: github: auth: oauth: client_id: "your-github-client-id" client_secret: "your-github-client-secret" access_token: "your-github-access-token"

也可以不写文件,改用同名的环境变量注入,效果等同。

2. 获取 GitHub 访问令牌并导出

可以通过 examples/oauth 下的交互式示例先走一遍完整授权拿到新令牌,也可以在 GitHub 的 Settings → Developer settings → Personal access tokens 中创建。示例代码(client.py)会校验令牌前缀是否为gho_ghp_github_pat_三者之一,并在缺失时打印获取指引。拿到后导出环境变量:

export GITHUB_ACCESS_TOKEN="github_pat_xxx"

3. 安装依赖

从仓库根目录安装项目及可选 Redis 支持:

pip install -e . # 可选:Redis 后端支持 # pip install -e .[redis]

4.(可选)启用 Redis 持久化

默认令牌只保存在内存中,进程重启即丢失。如需持久化(例如多个 Temporal worker 进程需要共享令牌),启动一个 Redis 并设置OAUTH_REDIS_URL

docker run --rm -p 6379:6379 redis:7-alpine export OAUTH_REDIS_URL="redis://127.0.0.1:6379"

main.py启动时会读取该环境变量并覆盖默认的 token store 配置(见下文)。

服务端配置逐项解析

mcp_agent.config.yaml 是本示例的服务端配置核心,它同时声明了执行引擎、OAuth 全局参数与 GitHub MCP 服务器的认证方式:

$schema: ../../../schema/mcp-agent.config.schema.json execution_engine: temporal temporal: host: localhost:7233 namespace: default task_queue: mcp-agent max_concurrent_activities: 10 logger: transports: [console, file] level: info path_settings: path_pattern: "logs/mcp-agent-{unique_id}.jsonl" unique_id: "timestamp" oauth: 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: false

关键配置项说明:

  • execution_engine: temporal:声明工作流由 Temporal 执行引擎调度,因此必须配合temporal.host/namespace/task_queue指向一个可用的 Temporal 服务(默认localhost:7233),并由 worker.py 启动 worker 消费任务。
  • oauth.loopback_ports:客户端本地回环回调端口列表。从源码看,OAuthSettings 中还定义了flow_timeout_seconds(授权回调超时,默认 300 秒)、callback_base_url(使用内部回调时的基础 URL)等参数,本示例采用use_internal_callback: false,即走本机回环端口接收授权回调。
  • mcp.servers.github.auth.oauth:下游 GitHub MCP 服务器的 OAuth 配置,enabled: true开启认证,scopes声明需要的权限范围(read:orgpublic_repouser:email),authorization_server指向 GitHub 的授权端点https://github.com/login/oauthinclude_resource_parameter: false表示授权请求不携带 resource 参数。

服务端启动逻辑:main.py 源码解读

main.py 的启动流程可拆解为四步:

1. 加载配置并注入 Redis 覆盖。启动时读取OAUTH_REDIS_URL环境变量,若存在则以backend="redis"redis_url=...覆盖settings.oauth.token_store;否则确保 token store 有默认值。这与 config.py 中OAuthTokenStoreSettings的定义对应:backend取值限定为memoryredis(默认memory),另有redis_prefix(默认mcp_agent:oauth_tokens)与refresh_leeway_seconds(默认 60 秒,即令牌到期前 60 秒触发刷新)。

2. 校验 GitHub OAuth 凭据。代码会检查settings.mcp.servers["github"].auth.oauth是否包含client_idclient_secret,缺失则直接抛出SystemExit并提示从mcp_agent.config.yamlmcp_agent.secrets.yaml提供。

3. 注册工作流任务与工具。@app.workflow_task(name="github_org_search_activity")定义一个 Temporal 活动,通过gen_client("github", ...)连接 GitHub MCP 服务器并调用search_repositories工具(query=f"org:{query}"per_page=5、按 best-match 排序),结果解析为 JSON 返回;@app.tool(name="github_org_search")则暴露一个普通 MCP 工具,内部通过app.executor.execute(...)把活动调度给 Temporal 执行,这正是「工具 → 工作流」的桥接点。

4. 以 SSE 模式启动 MCP 服务器。create_mcp_server_for_app(agent_app)为整个应用生成统一的 MCP 服务(会自动挂载workflows-store-credentials等内置工具),随后mcp_server.run_sse_async()监听 SSE 端点供客户端连接。配置中的session_id="workflow-pre-authorize"则用于把服务端会话绑定到固定的上下文。

核心工具workflows-store-credentials深度解析

客户端预置令牌调用的workflows-store-credentials并非示例自定义工具,而是 mcp-agent 服务端内置的能力,实现在 app_server.py 中。其签名与参数如下:

参数类型必填说明
workflow_namestr将使用这些令牌的工作流名称,必须是服务器上已注册的工作流
tokensList[Dict]令牌对象列表,至少一项

每个令牌对象支持的字段:

字段必填说明
access_tokenOAuth 访问令牌本体
server_name令牌对应的 MCP 服务器名称/标识,必须在服务器注册表中存在
refresh_tokenOAuth 刷新令牌
scopesOAuth 权限范围列表
expires_at令牌过期时间戳(float)
authorization_server授权服务器 URL,若提供会与配置比对校验

该工具的执行逻辑依次为:校验workflow_name是否已注册、token manager 是否可用;遍历tokens列表逐项校验access_tokenserver_name的完整性、server_name是否在app_context.server_registry.registry中可解析;随后调用app_context.token_manager.store_user_token(...)持久化。返回结果形如:

{ "success": true, "workflow_name": "github_org_search", "stored_tokens": 1, "total_tokens": 1 }

若部分令牌失败,返回中会增加partial_success: trueerrors数组;全部失败则抛出ToolError。完整校验过程见 app_server.py。

令牌落库:OAuthTokenManager.store_user_token 底层实现

workflows-store-credentials最终落到的store_user_token位于 oauth/manager.py,它负责把「客户端塞进来的裸令牌」转化为规范化的TokenRecord并写入 store,核心步骤包括:

  1. 校验服务器启用了 OAuth:若目标服务器的auth.oauth未配置或enabled为假,抛出OAuthFlowError
  2. 解析 OAuth 上下文:通过_resolve_oauth_context根据服务器配置、请求的 scopes 解析出资源标识与授权服务器 issuer;
  3. 校验授权服务器一致性:若调用方提供了authorization_server字段,会将其规范化后与解析出的 issuer 比对,不一致直接拒绝——防止令牌被「错放」到错误的授权方;
  4. Scope 合规检查:若存储的令牌缺失配置中要求的 scopes,会记录告警日志;令牌实际生效的 scopes 取调用方提供的与配置声明的并集;
  5. 构建TokenRecord写入 store:记录中写入access_token、可选的refresh_tokenscopesexpires_attoken_type(默认Bearer)、authorization_serverobtained_at时间戳,并在metadata中保留server_nameauthorization_server_urlworkflow_namesession_id等溯源信息。

此后,工作流执行过程中 token manager 通过get_access_token_if_present按「调用方显式身份 → 当前请求身份 → 会话身份 → 默认身份」的优先级解析用户,命中缓存即直接使用,从而跳过交互式授权(oauth/manager.py)。

客户端调用链:client.py 源码解读

client.py 展示了客户端侧的标准写法:

1. 创建应用上下文。MCPApp(name="workflow_mcp_client", human_input_callback=..., elicitation_callback=...)初始化,app.run()进入运行上下文后即可访问context.server_registry

2. 注册远程服务器。通过代码向context.server_registry.registry注入名为pre_authorize_serverMCPServerSettings,指明transport="sse"url="http://127.0.0.1:8000/sse",指向本地工作流服务器(注释中还保留了用uv run main.py以 stdio 方式启动的备选方案)。

3. 建立会话并启用日志。自定义ConsolePrintingClientSession(继承MCPAgentClientSession)在_received_notification中打印服务端发来的非日志通知,并传入logging_callback以实时输出服务端日志;连接建立后调用set_logging_level("info")(老版本服务器不支持时静默降级)。

4. 预置令牌。默认(除非带--skip-store-credentials参数)调用:

await server.call_tool( "workflows-store-credentials", arguments={ "workflow_name": "github_org_search", "tokens": [ { "access_token": access_token, "server_name": "github", } ], }, )

这里正是「先种凭据」的一步:把环境变量里的 GitHub 令牌与github服务器绑定,登记到github_org_search工作流名下。

5. 触发工作流。紧接着调用github_org_search工具并传入查询词(示例中为lastmile-ai),把返回内容尝试按 JSON 解析后美化打印。

另外注意 client.py 中对 stdio 客户端关闭阶段BrokenResourceError良性竞态的容错处理:仅当异常为BrokenResourceError或全部子异常均为该类型时才忽略,否则照常抛出,避免关闭过程被误判为故障。

完整运行流程

按以下顺序启动,即可观察到「先授权、后执行」的完整链路:

第一步:启动 Temporal 服务(若本地尚未运行)。Temporal 是工作流执行引擎,mcp_agent.config.yamltemporal.host: localhost:7233指向它。

第二步:启动工作流服务器

python examples/oauth/pre_authorize/main.py

该进程会校验 OAuth 凭据、挂载 MCP 服务并监听http://127.0.0.1:8000/sse

第三步(可选但推荐):启动 Temporal worker

python examples/oauth/pre_authorize/worker.py

worker.py通过create_temporal_worker_for_app(app)创建并运行 worker,负责消费mcp-agent任务队列中的活动。

第四步:运行客户端(另开终端):

python examples/oauth/pre_authorize/client.py

客户端依次完成:连接 SSE 服务器 → 调用workflows-store-credentials缓存 GitHub 令牌 → 调用github_org_search工作流 → 工作流经 Temporal 执行活动 → 活动使用缓存令牌查询 GitHub MCP 服务器并返回仓库列表。

若要验证「跳过预置令牌」的行为,可运行:

python examples/oauth/pre_authorize/client.py --skip-store-credentials

此时客户端只触发工作流而不种令牌,可用于对照观察缓存缺失时的表现。

设计要点与扩展建议

  • 存储后端选择:内存后端适合单进程演示;一旦涉及多个 Temporal worker 进程共享令牌,务必启用 Redis(backend: "redis"+OAUTH_REDIS_URL),否则不同进程会各自维护一份互不可见的缓存。specify-secrets.mdx 明确指出这正是让令牌跨重启保持持久、供后台工作流安全取用的方式。
  • 身份与作用域校验store_user_token对授权服务器 URL、scopes 均做了显式校验与告警,种令牌时建议始终携带与配置一致的scopes和(如适用)authorization_server,避免令牌落库后因 scope 缺失而在运行期才暴露问题。
  • 与交互式授权的互补examples/oauth/interactive_tool走的是「运行时按需弹浏览器授权」路线,pre_authorize则是「运行前批量种令牌」路线。二者共用同一套settings.oauth与 token store 配置(server-authentication.mdx),可根据执行环境是前台交互还是后台批处理来选择。
  • 进一步阅读:完整的认证体系可参考 authentication.mdx,密钥管理规范见 specify-secrets.mdx,Temporal 执行引擎的选型依据见 execution-engine.mdx。

小结

examples/oauth/pre_authorize用不到百行的服务端与客户端代码,示范了一条对异步工作流至关重要的认证路径:把「需要用户在场的交互式授权」前置为「可离线完成的令牌预置」,再用共享 token store 让后台 Temporal 工作流无感消费。配合workflows-store-credentials内置工具的参数校验、store_user_token的规范化落库以及 Redis 持久化,这套模式可以直接平移到你自己的 mcp-agent 项目中,解决后台代理、定时任务与长时间工作流的受保护资源访问问题。

【免费下载链接】mcp-agentBuild effective agents using Model Context Protocol and simple workflow patterns项目地址: https://gitcode.com/GitHub_Trending/mc/mcp-agent

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询