fix(ui_get): bound pagination to cap latency#2766
Merged
Merged
Conversation
ui_get backs a synchronous UI picker (label/assignee/etc. dropdowns in the MCP App issue/PR write surfaces). Each handler paginated GitHub API results in an unbounded loop (PerPage 100, looping until NextPage==0 / HasNextPage==false). On very large repos/orgs this fans out into dozens of sequential round-trips, and a single slow page inflates the whole call — production telemetry showed a tail spiking to ~20 minutes. Bound every ui_get pagination loop to uiGetMaxPages (10 pages, ~1000 items) and add an additive "has_more" flag indicating results were truncated. Truncation is acceptable here because the picker pairs it with typeahead, so responsiveness matters more than completeness. Affected methods: labels, assignees, milestones, branches, reviewers (both the collaborators and teams loops). uiGetIssueTypes and issue_fields are single-request and left unchanged. has_more is response-only and backward-compatible: no existing keys are removed or renamed, and the tool input schema is unchanged. HTTP-client timeouts are intentionally a separate follow-up. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR bounds pagination in the ui_get tool’s synchronous picker endpoints to cap tail latency on large repos/orgs, and surfaces intentional truncation to the UI via an additive has_more response field.
Changes:
- Introduces a shared
uiGetMaxPagescap and applies it to all paginatedui_gethandlers (REST and GraphQL). - Adds
has_moreto paginated handler responses to indicate deliberate truncation when the page cap is hit. - Extends
pkg/github/ui_tools_test.gowith within-cap and truncation test cases (including a custom transport for multi-page GraphQL label queries).
Show a summary per file
| File | Description |
|---|---|
| pkg/github/ui_tools.go | Caps ui_get pagination loops and emits has_more when results are truncated due to the cap. |
| pkg/github/ui_tools_test.go | Adds/extends tests to validate the page cap behavior and has_more semantics for bounded and truncated cases. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 0
omgitsads
approved these changes
Jun 25, 2026
SamMorrowDrums
approved these changes
Jun 25, 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
ui_getbacks a synchronous UI picker — the label/assignee/milestone/branch/reviewer dropdowns in the MCP App issue/PR write surfaces. Each handler paginated GitHub API results in an unbounded loop (PerPage: 100, looping untilNextPage == 0/HasNextPage == false).On very large repos/orgs this fans out into dozens of sequential API round-trips, and a single slow/stalled page inflates the whole call. Production telemetry showed
ui_getp50 at sub-second but the tail spiking to ~20 minutes (max/p99 ~1.17M–1.29M ms).Fix
Bound every
ui_getpagination loop to a shared package-level constantuiGetMaxPages = 10(≈1000 items atPerPage100) and add an additivehas_moreboolean to each response indicating results were deliberately truncated. The tool is now fast-or-bounded instead of unbounded.Truncation is an accepted product behavior here: the picker pairs it with typeahead, so responsiveness matters more than completeness.
has_moreis computed as "there were more pages we deliberately did not fetch":resp.NextPage != 0.PageInfo.HasNextPagestill true.uiGetReviewers: true if either the collaborators loop or the teams loop was truncated.Affected methods
uiGetLabelstotalCountstays the server-reported full repo count, solen(labels) < totalCountwhen truncated — expecteduiGetAssigneestotalCount = len(result)(bounded count)uiGetMilestonestotalCount = len(result)uiGetBranchestotalCount = len(result)uiGetReviewershas_moreuiGetIssueTypesandissue_fieldsare single-request (no loop) and intentionally left unchanged.