Skip MCP App form when issue/PR write carries non-form params#2589
Merged
mattdholloway merged 2 commits intoJun 1, 2026
Conversation
When MCP Apps are enabled and the client supports UI, issue_write and create_pull_request route the call to an interactive form. The form only collects a subset of fields and rebuilds the submit payload from scratch, so any parameter it cannot represent was silently dropped — e.g. labels, assignees, milestone, type, state and issue_fields (priority) for issue_write. Skip the form and execute directly whenever the call carries a parameter outside the set the form collects and re-sends. This generalizes the previous state-only guard and is robust to future parameter additions (an unrecognized param now bypasses the form rather than being lost). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a data-loss bug where issue_write and create_pull_request MCP App UI form interception would silently drop agent-supplied parameters (e.g. labels, assignees, issue_fields) that the form does not collect or re-send. The fix introduces explicit allow-lists of form-collected params per tool and bypasses the UI form whenever any other parameter is present, generalizing the previous narrow state-only guard.
Changes:
- Add
issueWriteFormParams/pullRequestWriteFormParamssets and*HasNonFormParamshelpers; include them in the UI-interception condition. - Simplify the
issue_writeupdate branch (state-specific guard removed; now subsumed by the general non-form-params check) in both theissue_fieldsvariant and the legacy variant. - Add table-driven helper tests and behavioral subtests verifying the bypass path for
issue_fields/labels(issue_write) and a non-form param (create_pull_request).
Show a summary per file
| File | Description |
|---|---|
| pkg/github/issues.go | Add form-param allow-list + helper; replace state-only bypass with general non-form-params bypass in both IssueWrite variants |
| pkg/github/issues_test.go | New helper unit tests and UI-gate behavioral tests for issue_fields and labels bypass |
| pkg/github/pullrequests.go | Add form-param allow-list + helper for create_pull_request; add to UI-interception guard |
| pkg/github/pullrequests_test.go | New helper unit tests and UI-gate behavioral test for non-form param bypass |
Copilot's findings
- Files reviewed: 4/4 changed files
- Comments generated: 0
The issue-write and pr-write forms rebuilt their submit payload from scratch, so any parameter the form does not render was dropped on submit. Spread the original toolInput first and override only the edited fields, so unsupported params (e.g. issue_fields, labels, state) are preserved when the user submits the form. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
RossTarrant
approved these changes
Jun 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When MCP Apps are enabled and the client supports UI,
issue_writeandcreate_pull_requestreturn a "Ready to create… click Submit" stub and route the call to an interactive form before parsing the rest of the parameters.The form then rebuilt its submit payload from scratch and only re-sent the fields it collects. For
issue_writethe form only handlestitle/body(andissue_numberon update), so any agent-suppliedlabels,assignees,milestone,type,state,state_reason,duplicate_oforissue_fields(e.g. priority) were silently dropped.This was surfaced by a partner team testing the new issue-fields support: setting priority via the model never took effect when the UI form intercepted the call.
Fix
Two complementary layers so no agent-supplied parameter is ever lost:
1. Server: skip the form for non-form params (
pkg/github)Each tool declares an allowlist of the params its form collects/re-sends (
issueWriteFormParams/pullRequestWriteFormParams). A helper returnstrueif the call carries any other non-nil param, and that is added to the interception condition so the call executes directly instead of being routed to the form.issue_write(both theissue_fields-enabled and legacy variants)create_pull_requestThis generalizes the previous
state-only guard (same behaviour preserved) and is allowlist-based, so it is robust to future parameters — any newly-added param that the form does not yet support automatically bypasses the form rather than being dropped.Handler-only change — no tool schema changes, so toolsnaps and generated docs are unaffected.
2. UI: forward original params on submit (
ui/src/apps)The issue-write and pr-write forms now spread the original
toolInputfirst and override only the edited fields (title/body/etc.), so any parameter the form does not render is preserved when the user submits. This is a safety net for any params that do reach the form (e.g. if the server guard is later relaxed, or for params the form renders only partially).Tests
issue_fields/labels(issue_write) or a non-form param (create_pull_request) skips the form and executes directly.