Skip to content

[Follow-up] MCP OAuth login lifecycle:mcpServer/oauth/login -> oauthLogin/completed #670

Description

@kxn

父 issue

背景

当前 upstream openai/codex app-server(main @ 9e552e9d15ba52bed7077d5357f3e18e330f8f38, 2026-07-12 复核)明确提供独立 OAuth lifecycle:

  • mcpServer/oauth/login
    • client-initiated JSON-RPC request
    • 参数:name,可选 threadId / scopes / timeoutSecs
    • 响应:{ authorization_url }
  • mcpServer/oauthLogin/completed
    • async notification
    • payload:{ name, threadId, success, error? }

本仓库目前对这条 lifecycle 完全未建模:

  • 没有 command translation 去主动发 mcpServer/oauth/login
  • 没有 response/event translation 去承接 authorization_url
  • 没有 notification translation 去消费 mcpServer/oauthLogin/completed
  • Feishu / daemon 没有最小可用的显式发起入口与完成/失败收敛提示

这条 lifecycle 不是 mcp_server_elicitation 的子形态:mcp_server_elicitation 是 server-initiated pending request;OAuth login 是 client-initiated RPC + async notify。

目标

  • mcpServer/oauth/login -> mcpServer/oauthLogin/completed 建立独立本地 contract。
  • 明确它与 mcp_server_elicitation 的职责边界,避免继续混模。
  • 为 Feishu / relay / headless 远端 surface 补齐最小可用 OAuth continuation 生命周期:用户可以主动发起、看到授权 URL,并在完成后收到成功/失败结果。

范围

  • agentproto command / event DTO:能表达 OAuth login 发起、授权 URL 返回、完成通知。
  • Codex translator:把本地 command 映射为 upstream mcpServer/oauth/login,并把 response / notification 翻译回本地 event。
  • daemon / Feishu 入口:新增最小 slash command /mcpoauth <server>,用于对当前接管实例发起某个 MCP server 的 OAuth 登录。
  • 用户可见收敛:授权 URL 用单次卡片/notice 展示;oauthLogin/completed 用成功/失败 notice 收口。
  • 文档、状态机说明和回归测试同步。

非目标

  • 不把 OAuth login 塞入 pending request substrate。
  • 不改普通 mcp_server_elicitation form/url request 的现有交互。
  • 不实现 MCP account/admin UI、server 列表选择器或配置编辑器。
  • 不在本单处理 item/permissions/requestApprovalapp/listmcpServer/startupStatus/updated 等其他 app-server backlog。
  • 不做打字机、流式 URL 展示或频繁卡片 patch;飞书侧只展示完整 URL 与最终结果。

完成标准

  • 本仓库能显式区分“elicitation request”与“OAuth login RPC lifecycle”。
  • /mcpoauth <server> 至少在已接管实例下能发起 mcpServer/oauth/login
  • authorization_url 能稳定展示给当前 Feishu surface,且不会依赖 request-card flow。
  • mcpServer/oauthLogin/completed 不再是未消费上游噪音;成功/失败都会向可定位的 surface 收敛。
  • response error、command ack reject、relay dispatch failure 都有用户可见失败提示。
  • 相关协议文档和状态机文档记录本地承接策略。

依赖

相关文档

  • docs/general/codex-mcp-app-server-protocol.md
  • docs/inprogress/codex-app-server-state-machine-audit.md
  • docs/general/remote-surface-state-machine.md
  • docs/general/feishu-card-ui-state-machine.md
  • upstream codex-rs/app-server/README.md
  • upstream codex-rs/app-server/src/request_processors/mcp_processor.rs
  • upstream codex-rs/app-server/tests/suite/v2/executor_mcp.rs

涉及文件

  • internal/core/agentproto/types.go
  • internal/core/agentproto/wire.go
  • internal/adapter/codex/translator.go
  • internal/adapter/codex/translator_commands.go
  • internal/adapter/codex/translator_observe_server.go
  • internal/adapter/codex/translator_*_test.go
  • internal/core/control/types.go
  • internal/core/control/feishu_commands.go
  • internal/core/control/feishu_commands_parse.go
  • internal/core/orchestrator/service_surface_actions.go
  • internal/app/daemon/app_ingress.go
  • internal/app/daemon/app_ui.go
  • internal/app/daemon/app_*_test.go
  • docs/general/codex-mcp-app-server-protocol.md
  • docs/inprogress/codex-app-server-state-machine-audit.md
  • docs/general/remote-surface-state-machine.md
  • docs/general/feishu-card-ui-state-machine.md

信息索引

  • Upstream request method:mcpServer/oauth/login
  • Upstream request params:name、optional threadId、optional scopes、optional timeoutSecs
  • Upstream response field:authorization_url
  • Upstream completion notify:mcpServer/oauthLogin/completed
  • Completion payload:namethreadIdsuccess、optional error
  • Non-streamable server upstream error:OAuth login is only supported for streamable HTTP servers.

调研结论

  • mcp_server_elicitation 当前已经是独立 request family,但它解决的是 server 向用户要额外输入 / URL continue 的 request-response 问题。
  • mcpServer/oauth/login 是另一类 client-initiated RPC + async notify,不属于 pending request substrate。
  • 本地当前没有任何 oauth/login / oauthLogin/completed 代码路径;只在审计文档中记录了缺口。
  • Feishu 侧不适合做流式展示;本单只展示完整授权 URL 和最终完成结果。

执行决策

  • 是否拆分:否。
  • 不拆分原因:本单目标集中在一条上游 lifecycle 的最小承接,虽然横跨 protocol / translator / daemon / Feishu 入口,但验证面仍是同一条发起-URL-完成链路,可以作为单 worker 执行单元推进。
  • 当前执行单元:[Follow-up] MCP OAuth login lifecycle:mcpServer/oauth/login -> oauthLogin/completed #670
  • 是否需要独立 verifier:需要;这是协议集成 + 用户可见 Feishu 行为,完成后默认做独立 read-only verifier pass,再 close。
  • 产品决策:当前无需再等待产品拍板。第一阶段采用显式 /mcpoauth <server>,不做菜单主入口、不做账号管理 UI、不做流式卡片更新。

建议范围

阶段 A:协议 contract 与 translator

  • 新增 agentproto.CommandMCPOAuthLogin
  • 新增 OAuth login command payload:server name、可选 thread id、可选 scopes、可选 timeout seconds。
  • 新增 OAuth login event payload:authorization URL ready 与 completed 两类事件。
  • TranslateCommand 生成 upstream mcpServer/oauth/login JSON-RPC request,参数字段使用 upstream 原生 casing:namethreadIdscopestimeoutSecs
  • translator 记录 request id 到 command origin / server / thread 的 pending 映射;response 成功时发出 URL-ready event,response error 时发出 system.error
  • completion notification 只按协议字段相关:name + threadId 匹配 pending OAuth flow,不能用时间窗口或“当前 thread”猜测。

阶段 B:Feishu / daemon 最小入口

  • 在 Feishu slash command registry 中加入 /mcpoauth <server>,默认不放进主菜单。
  • 在当前 surface 已接管实例时发起 CommandMCPOAuthLogin
  • 当前 surface 有 selected thread 时传 threadId;没有 selected thread 时允许 app-scoped login(省略 threadId),由 upstream 按最新全局配置解析。
  • 对缺少 server 参数、未接管实例、dispatch failure、command ack reject 给出明确 notice。

阶段 C:用户可见 URL 与完成收敛

  • URL-ready event 回到发起 surface,发送一次授权链接 notice/card;不进入 request-card flow。
  • completed event 成功时发送“认证已完成” notice。
  • completed event 失败时发送“认证失败” notice,并包含 upstream error 的简短摘要。
  • 若 completion 找不到发起 surface,优先按 thread / attached surface 做保守路由;仍无法定位则只记录为协议事件,不广播到无关 chat。

阶段 D:文档、状态机和回归

  • 更新 docs/general/codex-mcp-app-server-protocol.md:记录本地 command/event contract 与 upstream method / notify 对应关系。
  • 更新 app-server 状态机审计文档:把 mcpServer/oauth/login -> ...completed 从“完全未建模”推进到“最小支持”。
  • 如实现触及 remote route/gate 或 Feishu 卡片承载,运行对应 guardrail 并同步 canonical docs。
  • 回卷 [Follow-up] MCP server elicitation 独立 request family:表单 / OAuth 继续流审视 #668 总调度表与结果结论。

实现参考

  • 不复用 CommandRequestRespondRequestPrompt;OAuth login 是主动 command,不是 request response。
  • 建议在 agentproto.Command 上新增专用 payload 字段,例如 MCPOAuthLogin,避免把 server name 塞进 Request / Prompt / Metadata
  • 建议新增事件 kind,例如 mcp.oauth_login.authorization_urlmcp.oauth_login.completed,并用同一个 payload struct 承载 server、thread、command、URL、success/error。
  • translator pending 状态至少保存:CommandIDInitiator / origin surface、server name、thread id。
  • completion notification 缺 request id,相关性只能来自 name + threadId 和 pending command state;严禁 thread-local / timing heuristic。
  • 第一阶段可以序列化同一 instance + server + thread 的 OAuth flow,避免 completion notify 无法区分并发同 key 登录。
  • 用户可见 URL 不要求 open-url button;如果用 notice 文本展示链接更小更稳,后续再决定是否升级成专用 page/card。
  • Feishu 文案只说“MCP 服务认证 / 授权链接 / 完成 / 失败”,不要暴露 JSON-RPC method、request id 或上游内部字段。

检查参考

  • translator command test:CommandMCPOAuthLogin 输出 method mcpServer/oauth/login,params 字段和 upstream casing 正确。
  • translator response test:response { authorization_url } 生成 URL-ready event,并带回 command id / origin / server / thread。
  • translator error test:JSON-RPC error 生成 system.error,不会误发 URL-ready。
  • translator notification test:mcpServer/oauthLogin/completed success/failure 生成 completed event,并用协议字段相关到 pending flow。
  • daemon / Feishu parse test:/mcpoauth docs 解析为专用 action;空 server 参数返回 usage notice。
  • daemon dispatch test:已接管实例时发送 CommandMCPOAuthLogin,selected thread 存在时传 thread id。
  • daemon result test:URL-ready / completed success / completed failure 均发到发起 surface。
  • 回归:普通 mcp_server_elicitation form/url/approval 流不受影响。

建议执行测试面:

  • go test ./internal/adapter/codex
  • go test ./internal/core/control ./internal/core/orchestrator ./internal/app/daemon
  • go test ./internal/adapter/feishu
  • go test ./...
  • git diff --check
  • bash scripts/check/go-file-length.sh

收尾参考

当前阶段

finish-ready(实现、验证、文档同步与 verifier 均已完成;等待 commit / safe-push / finish-close)

当前执行点

  • CommandMCPOAuthLogin / EventMCPOAuthLoginURLReady / EventMCPOAuthLoginCompleted 已实现。
  • Codex translator 已支持 mcpServer/oauth/login request、authorization_url response、JSON-RPC error、mcpServer/oauthLogin/completed notification。
  • daemon / Feishu 已支持 /mcpoauth <server>/mcp-oauth <server>,help-visible、menu-hidden。
  • URL-ready、completed success/failure、dispatch failure、ack reject、response system.error 均收敛到发起 surface;未相关 completion 不广播。
  • 协议文档、审计文档、remote/Feishu 状态机文档已同步当前行为。
  • 独立 verifier 结果:pass。

已完成

  • 已复核 upstream Codex app-server main @ 9e552e9d15ba52bed7077d5357f3e18e330f8f38 的 OAuth login request / response / notification 形态。
  • 已确认这条 lifecycle 不复用 mcp_server_elicitation pending request substrate。
  • 已新增本地 agentproto command/event contract。
  • 已新增 translator pending flow:发起前记录 pending,response success 保留 pending 等 completion,response error 清理 pending。
  • 已新增 completion correlation:只按 pending serverName + threadID 匹配,不使用 timing/current thread heuristic。
  • 已新增 daemon pending flow:dispatch failure rollback,ack reject 清理,system.error 清理,completion 终态清理。
  • 已新增 /mcpoauth <server>/mcp-oauth <server> slash alias;不进入主菜单。
  • 已新增回归测试覆盖 translator、orchestrator command、daemon dispatch/pending/ack/error/URL/completion/uncorrelated completion。
  • 已同步 docs/general/codex-mcp-app-server-protocol.mddocs/inprogress/codex-app-server-state-machine-audit.mddocs/general/remote-surface-state-machine.mddocs/general/feishu-card-ui-state-machine.md

下一步

commit 当前实现,使用 ./safe-push.sh 推送,然后运行 issuectl finish --issue 670 --close

最后一致状态

  • #669 已关闭并回卷父单;approval-carrying elicitation once/session contract 已完成。
  • #670 已完成 OAuth login lifecycle 的最小本地支持。
  • 本地验证已通过:focused packages、go test ./...git diff --checkbash scripts/check/go-file-length.sh
  • 父单 #668 已补充 #670 结果回卷与父单 close judgment。

当前 blocker

无硬 blocker。

未完成尾项

  • commit / safe-push。
  • issuectl finish --issue 670 --close
  • #668 父单 close-plan / finish-close。

恢复步骤

  1. 运行 git status --short --branch 确认只有本任务改动。
  2. 如需重验,运行 go test ./...git diff --checkbash scripts/check/go-file-length.sh
  3. 提交当前实现并通过 ./safe-push.sh 推送。
  4. 运行 bash .codex/skills/issue-workflow-guardrail/scripts/issuectl.sh close-plan --issue 670 --format text
  5. 若 close-plan 仍为 ready,运行 bash .codex/skills/issue-workflow-guardrail/scripts/issuectl.sh finish --issue 670 --comment-file <file> --close
  6. 回到父单 #668 运行 close-plan / finish-close。

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:codexCodex protocol translation and wrapper integrationarea:daemonDaemon server, admin API, and runtime controlarea:feishuFeishu/Lark integration, gateway, projector, or previewenhancementNew feature or requeststatus:implementable-nowReady to implement with current issue body and context

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions