MCP Apps: isError form deferral suppresses PR forms in Codex desktop
Maintainers usually reply within 2 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- github, go
- Domain
- ai-infra-agents, backend-api-design
Research direction
Start by reading pkg/utils/result.go, especially NewToolResultAwaitingFormSubmission, and trace how form deferral sets IsError and structured status. Then follow the form result handler and host rendering lifecycle for pending, submitted, cancelled, and failed states; done means a deferred form renders and completes correctly without treating genuine tool failures as pending. The issue calls for testing submission, cancellation, failures, and final result delivery.
Written by the indexing model from the issue text.
Description
Describe the bug
On Codex desktop, GitHub MCP form deferral returns isError: true while instructing the assistant to wait for a form submission. The host classifies the call as failed and excludes the corresponding MCP App from rendering. The user therefore has no form to submit, the requested GitHub mutation never executes, and the assistant may end its turn waiting for an interaction that is unavailable.
This is a concrete mismatch between the server's pending-form signaling and the host's error-rendering policy. Related reports describe invisible or collapsing forms in Claude: #2823 and #3415. #3455 discusses retaining an opt-out. This report adds a Codex reproduction and a replay of the installed client's rendering gate; I have not established that the Claude reports have the same cause.
Affected version
- Server: hosted remote GitHub MCP at
https://api.githubcopilot.com/mcp/, not a local Docker deployment. - Version reported by MCP initialization during the controlled reproduction:
github-mcp-server/remote-a5f0ed2ba9b09e7e6bd87d7e8adb559158f83ff5. - Client: Codex desktop on macOS, version
26.1027.61615, build24141, production channel. - The app's updater reported
up_to_date; no desktop upgrade was applied during diagnosis. - Observed on November 3, 2026. Both PR creation and PR update encountered form deferral.
- The controlled MCP client advertised the
io.modelcontextprotocol/uiextension with MIME typetext/html;profile=mcp-app.
Steps to reproduce the behavior
- Connect the hosted GitHub MCP server to the affected Codex desktop version with MCP Apps support enabled and without the
mcp_apps_disable_form_deferralfeature flag. - Use an existing test PR that the authenticated account can edit. Read its current title.
- Call
update_pull_requestwith onlyowner,repo,pullNumber, and that same title. Using the unchanged title avoids an intentional content change. Example tool arguments:
{
"owner": "YOUR_OWNER",
"repo": "YOUR_TEST_REPO",
"pullNumber": 123,
"title": "THE_CURRENT_PR_TITLE"
}
- Observe a result with
isError: truewhose text asserts that a form has been shown and directs the assistant to wait silently for submission. - In the client, the call is marked failed and no usable form remains available to submit. The PR update has not executed.
The problem also occurred with create_pull_request. Reproducing with an existing PR is preferable to repeatedly attempting creation.
Expected vs actual behavior
Expected: Deferring to a form leaves a visible, usable form, accurately reports that the GitHub mutation is still pending, and eventually delivers submission success, failure, or cancellation to the assistant.
Actual: The pending-form response is treated as a failed tool result, which prevents form rendering. The response text asserts UI visibility without confirmation from the host. The assistant can then wait or stop while the user has nothing to submit.
Logs
Sanitized excerpt from an actual completed create-PR event; identifiers, arguments, and message text are omitted:
{
"type": "McpToolCall",
"server": "github",
"tool": "create_pull_request",
"mcpAppResourceUri": "ui://github-mcp-server/pr-write",
"status": "failed",
"result": {
"isError": true
}
}
The original result also contained a text block telling the assistant that a form was already displayed and to wait for submission. The corresponding resource-read RPC returned no protocol error, but that alone does not prove successful UI rendering.
Root-cause evidence
NewToolResultAwaitingFormSubmission deliberately sets IsError: true to stop the agent continuing. Its comment assumes the host will still render UI from the tool's metadata regardless of the result. The helper also supplies an awaiting_user_submission structured status in this source revision.
That assumption does not hold in the installed Codex build. Inspection of the installed JavaScript bundle found this chain:
- The tool-event projection passes the recorded
failedstatus into result normalization. - Normalization produces a result with
type: "error". - The MCP App eligibility/renderability logic explicitly excludes error results.
- The resource renderer returns its fallback branch when
shouldRenderMcpAppis false, even if valid HTML is available.
A local replay used the actual captured event and the unchanged normalization, error-detection, renderability, and resource-selection functions extracted from that installed bundle:
| Replay input | Normalized result | Form renderable | Resource selection |
|---|---|---|---|
Original recorded failed status |
error |
false |
fallback |
Only the recorded status changed to completed |
success |
true |
html |
This isolates a sufficient rendering blocker. The control changes the projected call status, not the live server response; it is not an end-to-end test of changing isError upstream. Unrelated eligibility predicates were allowed, text normalization was an identity operation for the captured text-only fixture, and the resource selector received synthetic valid HTML. No app binaries were modified. No live DOM instrumentation was performed, and additional UI problems are not ruled out.
The linked source revision is not claimed to be identical to the deployed remote build; the captured response independently establishes the problematic isError: true behavior.
Suggested fix
Please separate a successful handoff to a pending form from an actual tool error. One candidate is a non-error result carrying an explicit pending status and a clear indication that the GitHub mutation has not executed:
{
"isError": false,
"structuredContent": {
"status": "awaiting_user_submission",
"operationExecuted": false
}
}
The status already present in the helper is a useful starting point. Agents must not interpret a non-error handoff as a completed PR mutation. The client, form, and agent lifecycle should agree on pending, submitted, cancelled, and failed states. Also consider changing the response wording to describe a requested form handoff rather than asserting that the form is already visible.
This is a proposed direction, not a verified one-line fix: please test the form's own result handler, host rendering, submission, cancellation, genuine tool failures, and delivery of the final result. Simply allowing all failed-tool UIs or automatically executing an unsubmitted mutation would not address the underlying contract cleanly.
Verified workaround
The following request header avoids form deferral:
X-MCP-Features: mcp_apps_disable_form_deferral
Against the same remote server and the same unchanged-title PR update, the baseline request deferred with isError: true; the flagged request executed successfully. After restarting Codex to reload the header, an actual in-chat GitHub MCP PR update also succeeded without waiting for a form. This restores writes but does not repair form rendering.
- Dominant language
- Go
- Stars
- 34.2k
- Forks
- 5.3k
- Avg merge
- 5d 18h
- Merged PRs (30d)
- 13
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from github/github-mcp-server
-
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
github/github-mcp-server#3760 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
github/github-mcp-server#3758 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
github/github-mcp-server#3729 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
github/github-mcp-server#3722 ·
Maintainers usually reply within 2 days
-
Insiders Feedback: PR created UI doesn't link to the PRPossibly taken @genius-0963 claimed this 5 days ago. Openbug insiders user-experience
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
github/github-mcp-server#3658 ·
Maintainers usually reply within 2 days
All issues in github/github-mcp-server
Similar issues
-
ready-for-agent
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
hexlet-basics/hexlet-basics#962 ·
Maintainers usually reply within 1 day
-
imapproxy closeForMaintenance grace timer calls clientConn.Close() before closeMaintenanceTransportPossibly taken @dejanstrbac claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
renovate-health
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Netcracker/qubership-network-latency-exporter#267 ·
Maintainers usually reply within 1 day
-
Network capture does not workPossibly taken @KSpaceer claimed this today. Openkind/bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
aquasecurity/tracee#5395 ·