fix(server): MCP App forms now show when a write tool defers to them#23850
Problem
GitHub's MCP server (and any server built the same way) hands write tools like create_pull_request off to an MCP App form. It answers the first call with an isError result whose status is awaiting_user_submission, and expects the host to show the form. Codex reports an isError result as a failed call, and T3 only captured and showed MCP apps for completed calls. So the form never appeared, and the write could never happen.
Change
- The Codex adapter captures the app whenever the call has a result, not only when it completed, and passes
isError: trueon to the app for a failed call. - Web and mobile show a captured app whatever the call's status. Only the adapter attaches an app, and only once the call has a result.
Clicking submit in the form goes through the existing confirmation prompt for app tool calls, then the write runs.
Only Codex is affected. GitHub's server defers only when the client's initialize carries the io.modelcontextprotocol/ui extension (shouldDeferToForm). Claude Code 2.1.308 doesn't send it, so writes run directly there. What the test server logged from each client's initialize:
| 1 | claude-code ui extension: no |
| 2 | codex (via T3) ui extension: yes |
Scope and approval
A small fix for an obvious bug reported in #23825. MCP Apps already render inline in threads (#16236). This only stops a Codex isError result from hiding an app that the server asked the host to show. No new settings or workflows, and the existing confirm prompt still guards app-initiated writes.
Verification
Before / after (Linux, web UI, Codex)
I reproduced this with a small stdio MCP server that defers the same way GitHub's does (isError + awaiting_user_submission unless the call carries _ui_submitted: true).
Before (main): the call shows as a plain tool row. No form.
After: the form shows up with the title the agent passed in.
Clicking "Create note" asks for confirmation. After confirming, the server gets the _ui_submitted call and creates the note. Server log for the turn:
| 1 | tools/call create_note {"title":"Ship the fix"} -> isError, awaiting_user_submission |
| 2 | tools/call create_note {"title":"Ship the fix","_ui_submitted":true} -> Created note "Ship the fix". |
Tests
CodexAdapterV2.test.ts: a failed call with a result still gets its app captured, and the app seesisError: true. Fails on main.session-logic.test.ts: the web timeline shows the app for a failed call.
With GitHub's real MCP server (web, Codex)
github-mcp-server v2.0.3 (stdio, --toolsets=issues) against a throwaway private repo. The first issue_write call came back as GitHub's deferral (isError: true, "An interactive form has been shown to the user… The issue has NOT been created yet") and created nothing.
Before (fix reverted): no form, and no issue gets created.
After: GitHub's "Create issue" form shows in the thread.
Clicking "Create issue" asks for confirmation with the _ui_submitted: true call, and after confirming GitHub created the issue.
Mobile (Android, POCO F4, Codex, GitHub's real server)
A new thread started from the phone: the form shows, "Create issue" asks to allow the call, and GitHub created the issue. With only the mobile line reverted (server fix kept), the same thread shows no form, so the mobile change is needed.
| Form | Submitted | Mobile line reverted |
|---|---|---|
Not covered
After a page reload, a submitted thread shows the empty form again, because the app keeps its "submitted" state only in memory and T3 doesn't save app state. The confirm prompt still guards a second submit. Fixed in the stacked #24208.
Closes #23825
Opus 5.5, Claude Code in T3 Code.
🤖 Generated with Claude Code
Checks
- Successful
Skipped - Not affected
- SuccessfulCodeRabbitGitHub
Review completed
- SuccessfulNative fingerprint diffGitHub
- SuccessfulNative fingerprint diffGitHub
- SuccessfulNative fingerprint diffGitHub
- SuccessfulNative fingerprint diffGitHub
- SuccessfulCheckGitHub
- Skipped
- SkippedRustGitHub
- SkippedRelease SmokeGitHub
- SuccessfulBuild and TypecheckGitHub
- SuccessfulTest WebGitHub
- SuccessfulTestGitHub
- Skipped
- SuccessfulLabel PR 23850GitHub
- SuccessfulLabel PR sizeGitHub
- Skipped
- Successful
- Successful
- SuccessfulPrepare PR size configGitHub
- SkippedEAS PreviewGitHub
- SuccessfulCollect PR targetsGitHub
- SuccessfulTest Server 8GitHub
- SuccessfulTest Server 3GitHub
- SuccessfulTest Server 7GitHub
- SuccessfulTest Server 6GitHub
- SuccessfulTest Server 1GitHub
- SuccessfulTest Server 5GitHub
- SuccessfulTest Server 2GitHub
- SuccessfulTest Server 4GitHub
- SuccessfulChangesGitHub
- SuccessfulNative fingerprint diffGitHub
- SuccessfulLintGitHub
Added 1 commit
- fix(server): show MCP app forms when a write tool defers to them335e7e2
Approvability
Verdict: Approved at 335e7e2
Macroscope's review found this PR approvable — This is a focused MCP App rendering fix: Codex-deferred write calls marked failed can now retain their app form, while unrelated failed tools remain ordinary tool rows. The server and web behavior are covered by targeted tests, with the mobile change being the same narrow presentation condition.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review. 🎉
ℹ️ Recent review info
⚙️ Run configuration
- Source mode: Auto
- Repository: pingdotgg/t3code
- Branch: main
- File: .coderabbit.config.ts
- Commit: 2c8bdf6
- Layers: Repository TS config
- Review profile: CHILL
- Plan: Advanced
- Run ID:
cf256265-277c-4b36-b8d9-57ab76d86b3f
📥 Commits
Reviewing files that changed from the base of the PR and between 2c8bdf6177159d8fcd12a897d7c9311f6eb93237 and 335e7e2fdfc0daa8dfee7f6814c06f99678c25be.
📒 Files selected for processing: 5
apps/mobile/src/lib/threadActivity.tsapps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.tsapps/server/src/orchestration-v2/Adapters/CodexAdapterV2.tsapps/web/src/session-logic.test.tsapps/web/src/session-logic.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
📝 Walkthrough
The Codex adapter now captures MCP App resources for eligible failed results. The web and mobile clients now check dynamic-tool items for MCP Apps regardless of status.
Changes: Failed MCP App results
Layer / File(s) Summary Capture resources on failed results
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts,apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.tsThe adapter captures MCP App resources for results without errors, including failed items. It marks attached results as errors for failed items. A replay test checks the resource URI, error flag, and pending-work state. Extract MCP Apps in client timelines
apps/web/src/session-logic.ts,apps/mobile/src/lib/threadActivity.ts,apps/web/src/session-logic.test.tsThe web and mobile clients attempt MCP App extraction for dynamic-tool items regardless of status. A web test checks the MCP App entry for a failed GitHub tool call. Priority: ➖ Normal
Estimated code review effort: 2 (Simple) | ~10 minutes
Change: Bug fix · Severity of issue fixed: Medium
Suggested reviewers:
juliusmarminge,maria-rcks
Merge Risk: ⚪ Minimal · up to 335e7
The change enables deferred MCP App forms to appear after failed tool calls. No actionable merge-blocking risk is identified beyond normal validation.
Pre-merge checks: Passed 4
Check name Status Explanation Title check Passed The title is concise, specific, and accurately identifies the fix for MCP App forms deferred by write tools. Linked Issues check Passed Issue #23825asks that a deferred MCP App result either shows the form or shows an understandable deferred state with an escape hatch that keeps the tool result accessible. The PR takes the first opti…Out of Scope Changes check Passed All changed files serve the objective of showing deferred MCP App forms for failed-status tool calls. The CodexAdapterV2.ts change widens the app-capture condition and adds the isErrorflag. The ses…Description check Passed The description includes all required sections: Problem, Change, Scope and approval, and Verification. It explains the deferred MCP App failure, identifies the affected components, links issue #23825,…Full details: Linked Issues check
Explanation
Issue
#23825asks that a deferred MCP App result either shows the form or shows an understandable deferred state with an escape hatch that keeps the tool result accessible. The PR takes the first option. Verified at head: in CodexAdapterV2.ts, the app reference is now built wheneveritem.result != null && item.error == null, not only whenitem.status === "completed". Forstatus === "failed"the captured output setsresult: { ...item.result, isError: true }, so the app receives theisErrorflag. In session-logic.ts,deriveTimelineEntriesFromVisibleTurnItemscallsmcpAppFromToolItemfor everydynamic_toolitem, so the web timeline creates the app entry for a failed call. The mobile change in threadActivity.ts applies the same status-independent extraction. The added tests in CodexAdapterV2.test.ts and session-logic.test.ts cover a failed call with a deferred-form result. The author's before/after screenshots show the form rendering, the confirmation prompt, and the submitted write. The screenshots are not independently verifiable from the repository, so the PASS relies on the diff and the described tests. The mobile path has no device run; the issue marks mobile as unverified, so this does not block the verdict.
Full details: Out of Scope Changes check
Explanation
All changed files serve the objective of showing deferred MCP App forms for failed-status tool calls. The CodexAdapterV2.ts change widens the app-capture condition and adds the
isErrorflag. The session-logic.ts and threadActivity.ts changes remove thecompleted-only status gate in the web and mobile timelines. The CodexAdapterV2.test.ts and session-logic.test.ts additions test this behavior. No unrelated refactoring, feature, or configuration change appears in the reviewed diffs. The Codex-only scope matches the linked issue and the PR description.
Full details: Description check
Explanation
The description includes all required sections: Problem, Change, Scope and approval, and Verification. It explains the deferred MCP App failure, identifies the affected components, links issue
#23825, documents focused automated tests, manual web and mobile verification, screenshots, limitations, and agent details.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create a new PR
Comment @coderabbitai help to get the list of available commands.