MCP Apps: isError form deferral suppresses PR forms in Codex desktop

Open
#3,666 1 comment 0 reactions 0 assignees View on GitHub

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

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

bug insiders user-experience
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, build 24141, 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/ui extension with MIME type text/html;profile=mcp-app.
Steps to reproduce the behavior
  1. Connect the hosted GitHub MCP server to the affected Codex desktop version with MCP Apps support enabled and without the mcp_apps_disable_form_deferral feature flag.
  2. Use an existing test PR that the authenticated account can edit. Read its current title.
  3. Call update_pull_request with only owner, 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"
}
  1. Observe a result with isError: true whose text asserts that a form has been shown and directs the assistant to wait silently for submission.
  2. 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:

  1. The tool-event projection passes the recorded failed status into result normalization.
  2. Normalization produces a result with type: "error".
  3. The MCP App eligibility/renderability logic explicitly excludes error results.
  4. The resource renderer returns its fallback branch when shouldRenderMcpApp is 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

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from github/github-mcp-server

All issues in github/github-mcp-server

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.