Skip to content

Return assignees from list_issues - #3064

Open
tgockel wants to merge 1 commit into
github:mainfrom
tgockel:list-issues-assignees
Open

Return assignees from list_issues#3064
tgockel wants to merge 1 commit into
github:mainfrom
tgockel:list-issues-assignees

Conversation

@tgockel

@tgockel tgockel commented Aug 13, 2026

Copy link
Copy Markdown

The list_issues GraphQL fragment never selected assignees, so the tool could not report who an issue was assigned to. Its nearest field, user, is the issue author, which callers conflate with the assignee. Answering "is anything unassigned?" therefore cost one list_issues call plus one issue_read per candidate, and a truncated sweep invites a fabricated answer drawn from the author instead.

Add an assignees selection to IssueFragment, flatten it to logins in fragmentToMinimalIssue, and add "assignees" to listIssuesItemFieldEnum so it is selectable through fields. GitHub caps issue assignees at 10, so first: 100 cannot truncate; it also matches the page size already used for assignees in copilot.go.

Drop omitempty from MinimalIssue.Assignees and initialize the slice in both converters so an unassigned issue serializes as [] rather than an absent key, which is what lets a caller identify unassigned issues from a single response. This also affects issue_read, the other MinimalIssue consumer, which now reports "assignees": [] instead of omitting the key.

Summary

Adds assignees to list_issues, so the tool can report who each issue is assigned to. Previously the GraphQL fragment behind list_issues never selected assignees, and the field was absent from the tool's fields enum.

Why

list_issues had no way to return assignment data. Its fields enum offered number, title, body, state, user, labels, comments, created_at, updated_at, field_values — and user is the issue author, which callers conflate with the assignee.

This was an asymmetry between the two conversion paths rather than a deliberate omission. Both produce a MinimalIssue: the REST path (convertToMinimalIssue, backing issue_read) populated Assignees; the GraphQL path (fragmentToMinimalIssue, backing list_issues) never touched it. The enum was correct as documented — it lists only what the fragment actually populates — so the gap was upstream, in the query.

The cost was concrete: answering "is anything unassigned?" required one list_issues call plus one issue_read per candidate. On a 29-issue repository that is a 29-request sweep for a single question, and a sweep that gets truncated invites an answer fabricated from the user field instead.

Fixes #

What changed

  • pkg/github/issues.goIssueFragment gains an assignees(first: 100){nodes{login}} selection. All four list_issues query variants embed IssueQueryFragment, so one edit covers the labels/since/both permutations.
  • pkg/github/minimal_types.gofragmentToMinimalIssue flattens assignees to logins, matching the shape issue_read already emits.
  • pkg/github/minimal_types.go"assignees" added to listIssuesItemFieldEnum, and the invariant comment above it now records that user is the author while assignees is who it's assigned to.
  • pkg/github/minimal_types.goMinimalIssue.Assignees drops omitempty, and both converters initialize the slice non-nil, so an unassigned issue serializes as []. Both changes are required: omitempty alone drops the empty array, and dropping it without the init yields null.
  • Tests — new coverage for the populated and empty cases; the six hardcoded GraphQL query literals updated (githubv4mock matches the serialized query byte-for-byte).
  • pkg/github/__toolsnaps__/list_issues.snap — regenerated; one added enum entry.

MCP impact

  • No tool or API changes
  • Tool schema or behavior changed
  • New tool added

list_issues accepts a new "assignees" value in fields and returns it per issue. Because issue_read shares MinimalIssue, it is also affected: an unassigned issue now reports "assignees": [] rather than omitting the key. No field was removed or renamed.

Prompts tested (tool changes only)

  • "List the open issues in owner/repo and tell me which ones are unassigned"
  • "Who is assigned to each open issue in owner/repo?"
  • "Find an unassigned bug in owner/repo I could pick up"

Verified through the tool handler in unit tests (assigned, unassigned, fields-selected, and fields-omitted cases), and the new GraphQL selection was run against the live API. githubv4mock will happily match a query GitHub would reject, so mocks alone don't prove the selection is valid. A 100-issue sample returned 21 issues with assignees, max 2 each.

Security / limits

  • No security or limits impact
  • Auth / permissions considered
  • Data exposure, filtering, or token/size limits considered

No new data class or scope: assignee logins are already returned by issue_read, search_issues, and list_pull_requests, and assignees requires no permission beyond what listing issues already needs. Response growth is a short array of logins per issue, opt-out via fields. GitHub caps issue assignees at 10, so assignees(first: 100) cannot truncate; that page size matches the existing assignees(first: 100) selections in copilot.go.

Tool renaming

  • I am renaming tools as part of this PR (e.g. a part of a consolidation effort)
    • I have added the new tool aliases in deprecated_tool_aliases.go
  • I am not renaming tools as part of this PR

Lint & tests

  • Linted locally with ./script/lint
  • Tested locally with ./script/test

Docs

  • Not needed
  • Updated (README / docs / examples)

The list_issues GraphQL fragment never selected assignees, so the tool
could not report who an issue was assigned to. Its nearest field, user,
is the issue author, which callers conflate with the assignee. Answering
"is anything unassigned?" therefore cost one list_issues call plus one
issue_read per candidate, and a truncated sweep invites a fabricated
answer drawn from the author instead.

Add an assignees selection to IssueFragment, flatten it to logins in
fragmentToMinimalIssue, and add "assignees" to listIssuesItemFieldEnum so
it is selectable through fields. GitHub caps issue assignees at 10, so
first: 100 cannot truncate; it also matches the page size already used
for assignees in copilot.go.

Drop omitempty from MinimalIssue.Assignees and initialize the slice in
both converters so an unassigned issue serializes as [] rather than an
absent key, which is what lets a caller identify unassigned issues from
a single response. This also affects issue_read, the other MinimalIssue
consumer, which now reports "assignees": [] instead of omitting the key.
@tgockel
tgockel requested a review from a team as a code owner August 13, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant