### 父 issue - #668 ### 背景 当前 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/requestApproval`、`app/list`、`mcpServer/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 都有用户可见失败提示。 - 相关协议文档和状态机文档记录本地承接策略。 ### 依赖 - 父单 #668 的拆分结论。 - #669 已完成 approval-carrying elicitation 的 once/session contract,避免本单再混入 `mcp_server_elicitation` 内部语义。 - upstream Codex app-server 当前协议事实以 `main` @ `9e552e9d15ba52bed7077d5357f3e18e330f8f38` 为准。 ### 相关文档 - `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:`name`、`threadId`、`success`、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 执行单元推进。 - 当前执行单元:#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:`name`、`threadId`、`scopes`、`timeoutSecs`。 - 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。 - 回卷 #668 总调度表与结果结论。 ### 实现参考 - 不复用 `CommandRequestRespond` 或 `RequestPrompt`;OAuth login 是主动 command,不是 request response。 - 建议在 `agentproto.Command` 上新增专用 payload 字段,例如 `MCPOAuthLogin`,避免把 server name 塞进 `Request` / `Prompt` / `Metadata`。 - 建议新增事件 kind,例如 `mcp.oauth_login.authorization_url` 与 `mcp.oauth_login.completed`,并用同一个 payload struct 承载 server、thread、command、URL、success/error。 - translator pending 状态至少保存:`CommandID`、`Initiator` / 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` ### 收尾参考 - 完成实现后更新 #670 执行快照、验证结果和 verifier 结果。 - 子单关闭前,把 #670 的实现结果、验证结果、风险和未做项回卷到父单 #668。 - 若触及 Feishu card payload / inline replacement / command menu contract,运行 `feishu-ui-state-machine-guardrail` 并同步 `docs/general/feishu-card-ui-state-machine.md`。 - 若触及 remote routing / attached-instance / selected-thread / request gate,运行 `remote-state-machine-guardrail` 并同步 `docs/general/remote-surface-state-machine.md`。 - 若只是新增 slash command 且不进入主菜单,仍需检查 command registry / help / parse tests 是否一致。 - 完成后运行 `issuectl close-plan`,再按 workflow 做 verifier、commit、safe-push、finish-close。 ### 当前阶段 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.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`。 ### 下一步 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 --check`、`bash 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 --check`、`bash 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。
父 issue
背景
当前 upstream
openai/codexapp-server(main@9e552e9d15ba52bed7077d5357f3e18e330f8f38, 2026-07-12 复核)明确提供独立 OAuth lifecycle:mcpServer/oauth/loginname,可选threadId/scopes/timeoutSecs{ authorization_url }mcpServer/oauthLogin/completed{ name, threadId, success, error? }本仓库目前对这条 lifecycle 完全未建模:
mcpServer/oauth/loginauthorization_urlmcpServer/oauthLogin/completed这条 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的职责边界,避免继续混模。范围
agentprotocommand / event DTO:能表达 OAuth login 发起、授权 URL 返回、完成通知。mcpServer/oauth/login,并把 response / notification 翻译回本地 event。/mcpoauth <server>,用于对当前接管实例发起某个 MCP server 的 OAuth 登录。oauthLogin/completed用成功/失败 notice 收口。非目标
mcp_server_elicitationform/url request 的现有交互。item/permissions/requestApproval、app/list、mcpServer/startupStatus/updated等其他 app-server backlog。完成标准
/mcpoauth <server>至少在已接管实例下能发起mcpServer/oauth/login。authorization_url能稳定展示给当前 Feishu surface,且不会依赖 request-card flow。mcpServer/oauthLogin/completed不再是未消费上游噪音;成功/失败都会向可定位的 surface 收敛。依赖
mcp_server_elicitation内部语义。main@9e552e9d15ba52bed7077d5357f3e18e330f8f38为准。相关文档
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.mdcodex-rs/app-server/README.mdcodex-rs/app-server/src/request_processors/mcp_processor.rscodex-rs/app-server/tests/suite/v2/executor_mcp.rs涉及文件
internal/core/agentproto/types.gointernal/core/agentproto/wire.gointernal/adapter/codex/translator.gointernal/adapter/codex/translator_commands.gointernal/adapter/codex/translator_observe_server.gointernal/adapter/codex/translator_*_test.gointernal/core/control/types.gointernal/core/control/feishu_commands.gointernal/core/control/feishu_commands_parse.gointernal/core/orchestrator/service_surface_actions.gointernal/app/daemon/app_ingress.gointernal/app/daemon/app_ui.gointernal/app/daemon/app_*_test.godocs/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信息索引
mcpServer/oauth/loginname、optionalthreadId、optionalscopes、optionaltimeoutSecsauthorization_urlmcpServer/oauthLogin/completedname、threadId、success、optionalerrorOAuth 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代码路径;只在审计文档中记录了缺口。执行决策
/mcpoauth <server>,不做菜单主入口、不做账号管理 UI、不做流式卡片更新。建议范围
阶段 A:协议 contract 与 translator
agentproto.CommandMCPOAuthLogin。TranslateCommand生成 upstreammcpServer/oauth/loginJSON-RPC request,参数字段使用 upstream 原生 casing:name、threadId、scopes、timeoutSecs。system.error。name + threadId匹配 pending OAuth flow,不能用时间窗口或“当前 thread”猜测。阶段 B:Feishu / daemon 最小入口
/mcpoauth <server>,默认不放进主菜单。CommandMCPOAuthLogin。threadId;没有 selected thread 时允许 app-scoped login(省略threadId),由 upstream 按最新全局配置解析。阶段 C:用户可见 URL 与完成收敛
阶段 D:文档、状态机和回归
docs/general/codex-mcp-app-server-protocol.md:记录本地 command/event contract 与 upstream method / notify 对应关系。mcpServer/oauth/login -> ...completed从“完全未建模”推进到“最小支持”。实现参考
CommandRequestRespond或RequestPrompt;OAuth login 是主动 command,不是 request response。agentproto.Command上新增专用 payload 字段,例如MCPOAuthLogin,避免把 server name 塞进Request/Prompt/Metadata。mcp.oauth_login.authorization_url与mcp.oauth_login.completed,并用同一个 payload struct 承载 server、thread、command、URL、success/error。CommandID、Initiator/ origin surface、server name、thread id。name + threadId和 pending command state;严禁 thread-local / timing heuristic。instance + server + thread的 OAuth flow,避免 completion notify 无法区分并发同 key 登录。检查参考
CommandMCPOAuthLogin输出 methodmcpServer/oauth/login,params 字段和 upstream casing 正确。{ authorization_url }生成 URL-ready event,并带回 command id / origin / server / thread。system.error,不会误发 URL-ready。mcpServer/oauthLogin/completedsuccess/failure 生成 completed event,并用协议字段相关到 pending flow。/mcpoauth docs解析为专用 action;空 server 参数返回 usage notice。CommandMCPOAuthLogin,selected thread 存在时传 thread id。mcp_server_elicitationform/url/approval 流不受影响。建议执行测试面:
go test ./internal/adapter/codexgo test ./internal/core/control ./internal/core/orchestrator ./internal/app/daemongo test ./internal/adapter/feishugo test ./...git diff --checkbash scripts/check/go-file-length.sh收尾参考
feishu-ui-state-machine-guardrail并同步docs/general/feishu-card-ui-state-machine.md。remote-state-machine-guardrail并同步docs/general/remote-surface-state-machine.md。issuectl close-plan,再按 workflow 做 verifier、commit、safe-push、finish-close。当前阶段
finish-ready(实现、验证、文档同步与 verifier 均已完成;等待 commit / safe-push / finish-close)
当前执行点
CommandMCPOAuthLogin/EventMCPOAuthLoginURLReady/EventMCPOAuthLoginCompleted已实现。mcpServer/oauth/loginrequest、authorization_urlresponse、JSON-RPC error、mcpServer/oauthLogin/completednotification。/mcpoauth <server>与/mcp-oauth <server>,help-visible、menu-hidden。system.error均收敛到发起 surface;未相关 completion 不广播。已完成
main@9e552e9d15ba52bed7077d5357f3e18e330f8f38的 OAuth login request / response / notification 形态。mcp_server_elicitationpending request substrate。serverName + threadID匹配,不使用 timing/current thread heuristic。/mcpoauth <server>和/mcp-oauth <server>slash alias;不进入主菜单。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。下一步
commit 当前实现,使用
./safe-push.sh推送,然后运行issuectl finish --issue 670 --close。最后一致状态
#669已关闭并回卷父单;approval-carrying elicitation once/session contract 已完成。#670已完成 OAuth login lifecycle 的最小本地支持。go test ./...、git diff --check、bash scripts/check/go-file-length.sh。#668已补充#670结果回卷与父单 close judgment。当前 blocker
无硬 blocker。
未完成尾项
issuectl finish --issue 670 --close。#668父单 close-plan / finish-close。恢复步骤
git status --short --branch确认只有本任务改动。go test ./...、git diff --check、bash scripts/check/go-file-length.sh。./safe-push.sh推送。bash .codex/skills/issue-workflow-guardrail/scripts/issuectl.sh close-plan --issue 670 --format text。bash .codex/skills/issue-workflow-guardrail/scripts/issuectl.sh finish --issue 670 --comment-file <file> --close。#668运行 close-plan / finish-close。