Skip to content

Add GitHub UserList management tools - #3202

Open
ppoffice wants to merge 2 commits into
github:mainfrom
ppoffice:star-lists-userlist
Open

Add GitHub UserList management tools#3202
ppoffice wants to merge 2 commits into
github:mainfrom
ppoffice:star-lists-userlist

Conversation

@ppoffice

@ppoffice ppoffice commented Sep 2, 2026

Copy link
Copy Markdown

Summary

Adds GitHub UserList support to the existing stargazers toolset. UserLists are the lists available at github.com/stars for organizing repositories independently of star state.

Tools

  • list_user_lists: cursor-paginated list metadata with an optional bounded item preview.
  • list_user_list_items: cursor-paginated repositories for one list.
  • create_user_list
  • update_user_list
  • delete_user_list
  • add_repository_to_list
  • remove_repository_from_list

Design

  • List membership remains independent of star state; add/remove never star or unstar repositories.
  • updateUserListsForItem replaces complete membership, so mutations first derive and preserve existing memberships, including memberships found beyond the first item page.
  • Already-satisfied add/remove requests return without issuing a replacement mutation.
  • Item-inclusive reads require repository scope so private memberships are not silently omitted.
  • The write-capable user OAuth scope is supported but remains outside the default scope set.
  • User-list output is conservatively IFC-labeled private/trusted when IFC labels are enabled.
  • List and item reads are bounded and expose cursor metadata rather than eagerly draining unbounded nested connections.

Changes

  • GraphQL implementation and tool definitions in pkg/github/user_lists.go.
  • Scope policy, IFC label, inventory registration, toolsnaps, and README documentation.
  • Unit coverage for CRUD, name resolution, pagination, nullable descriptions, scope gating, missing repositories, replacement semantics, no-op mutations, and IFC metadata.
  • End-to-end coverage for create, rename, add, verify, remove, delete, and cleanup.

Verification

  • Focused user-list, scope, and IFC unit tests pass.
  • go vet -tags e2e ./e2e/ passes.
  • Package build passes.
  • TestUserLists passes against the live GitHub API in in-process e2e mode.

Add GraphQL-backed tools for listing, creating, updating, deleting, and managing repository membership in GitHub UserLists. Include bounded cursor pagination, private-membership scope checks, IFC labeling, unit coverage, tool snapshots, and generated documentation.
Exercise the complete UserList lifecycle against GitHub: create, rename, add a repository, verify membership, remove the repository, delete the list, and clean up the temporary repository.
@ppoffice
ppoffice requested a review from a team as a code owner September 2, 2026 19:09
Copilot AI balanced review requested due to automatic review settings September 2, 2026 19:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Membership replacement can overwrite concurrent changes, and the E2E pagination and cleanup issues must be addressed.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds GitHub UserList management, including paginated reads, CRUD operations, and repository membership tools.

Changes:

  • Adds seven UserList and membership tools.
  • Adds OAuth scope handling and IFC labels.
  • Adds documentation, snapshots, unit tests, and E2E coverage.
File summaries
File Description
README.md Documents the new tools.
pkg/scopes/scopes.go Supports the user OAuth scope.
pkg/scopes/scopes_test.go Tests scope registration.
pkg/ifc/ifc.go Adds UserList IFC labeling.
pkg/ifc/ifc_test.go Tests UserList labeling.
pkg/http/oauth/oauth_test.go Updates supported-scope expectations.
pkg/github/user_lists.go Implements UserList GraphQL tools.
pkg/github/user_lists_test.go Tests UserList behavior.
pkg/github/tools.go Registers the new tools.
pkg/github/tool_scopes.go Adds dynamic read-scope policy.
pkg/github/tool_scopes_test.go Tests scope metadata.
pkg/github/__toolsnaps__/update_user_list.snap Snapshots update schema.
pkg/github/__toolsnaps__/remove_repository_from_list.snap Snapshots remove schema.
pkg/github/__toolsnaps__/list_user_lists.snap Snapshots list schema.
pkg/github/__toolsnaps__/list_user_list_items.snap Snapshots item-list schema.
pkg/github/__toolsnaps__/delete_user_list.snap Snapshots delete schema.
pkg/github/__toolsnaps__/create_user_list.snap Snapshots create schema.
pkg/github/__toolsnaps__/add_repository_to_list.snap Snapshots add schema.
e2e/e2e_test.go Adds UserList lifecycle E2E coverage.
Review details

Suppressed comments (2)

pkg/github/user_lists_test.go:211

  • The GraphQL schema makes description nullable, but this response uses an empty string, so the new tests never exercise the normal null returned for a list without a description. Use null here (or add an equivalent case) to verify that decoding a description-less list succeeds, as claimed in the PR coverage.
				return http.StatusOK, `{"data":{"viewer":{"lists":{"nodes":[{"id":"list-2","name":"Second","description":"","isPrivate":true}],"pageInfo":{"hasNextPage":false,"hasPreviousPage":true,"startCursor":"second","endCursor":"second"},"totalCount":2}}}}`

pkg/github/user_lists_test.go:65

  • The PR summary states that nullable descriptions are covered, but both list cases return non-null strings and the update cases never exercise an explicitly empty description. Add a GraphQL response with "description": null for the read path and an update case with description: "" to verify both nullable decoding and clearing semantics.
									map[string]any{
										"id":          githubv4.ID("list-1"),
										"name":        githubv4.String("My list"),
										"description": githubv4.String("A list"),
										"isPrivate":   githubv4.Boolean(true),
  • Files reviewed: 19/19 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/github/user_lists.go
Comment on lines +443 to +446
input := githubv4.UpdateUserListsForItemInput{
ItemID: repoID,
ListIDs: result,
}
Comment thread e2e/e2e_test.go
Comment on lines +2018 to +2028
currentListName := listName
t.Cleanup(func() {
t.Logf("Cleaning up list %q...", currentListName)
resp, err := mcpClient.CallTool(ctx, &mcp.CallToolParams{
Name: "delete_user_list",
Arguments: map[string]any{"name": currentListName},
})
if err == nil && resp.IsError {
t.Logf("Cleanup: failed to delete list %q: %+v", currentListName, resp)
}
})
Comment thread e2e/e2e_test.go
Comment on lines +2105 to +2108
resp, err = mcpClient.CallTool(ctx, &mcp.CallToolParams{
Name: "list_user_lists",
Arguments: map[string]any{"include_items": true},
})
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.

2 participants