Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,7 @@ The following sets of tools are available:

- **pull_request_read** - Get details for a single pull request
- **Required OAuth Scopes**: `repo`
- `after`: Cursor for pagination, used only by the get_review_comments method. Pass the endCursor from the previous page's PageInfo to fetch the next page. (string, optional)
- `method`: Action to specify what pull request data needs to be retrieved from GitHub.
Possible options:
1. get - Get details of a specific pull request.
Expand Down
4 changes: 4 additions & 0 deletions pkg/github/__toolsnaps__/pull_request_read.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"description": "Get information on a specific pull request in GitHub repository.",
"inputSchema": {
"properties": {
"after": {
"description": "Cursor for pagination, used only by the get_review_comments method. Pass the endCursor from the previous page's PageInfo to fetch the next page.",
"type": "string"
},
"method": {
"description": "Action to specify what pull request data needs to be retrieved from GitHub. \nPossible options: \n 1. get - Get details of a specific pull request.\n 2. get_diff - Get the diff of a pull request.\n 3. get_status - Get combined commit status of a head commit in a pull request.\n 4. get_files - Get the list of files changed in a pull request. Use with pagination parameters to control the number of results returned.\n 5. get_review_comments - Get review threads on a pull request. Each thread contains logically grouped review comments made on the same code location during pull request reviews. Returns threads with metadata (isResolved, isOutdated, isCollapsed) and their associated comments. Use cursor-based pagination (perPage, after) to control results.\n 6. get_reviews - Get the reviews on a pull request. When asked for review comments, use get_review_comments method.\n 7. get_comments - Get comments on a pull request. Use this if user doesn't specifically want review comments. Use with pagination parameters to control the number of results returned.\n 8. get_check_runs - Get check runs for the head commit of a pull request. Check runs are the individual CI/CD jobs and checks that run on the PR.\n",
"enum": [
Expand Down
7 changes: 7 additions & 0 deletions pkg/github/pullrequests.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ Possible options:
Required: []string{"method", "owner", "repo", "pullNumber"},
}
WithPagination(schema)
// get_review_comments uses GraphQL cursor-based pagination and accepts the
// `after` cursor. Other methods rely on the `page`/`perPage` parameters
// added by WithPagination and ignore `after`.
schema.Properties["after"] = &jsonschema.Schema{
Type: "string",
Description: "Cursor for pagination, used only by the get_review_comments method. Pass the endCursor from the previous page's PageInfo to fetch the next page.",
}

return NewTool(
ToolsetMetadataPullRequests,
Expand Down
53 changes: 53 additions & 0 deletions pkg/github/pullrequests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,11 @@ func Test_GetPullRequestComments(t *testing.T) {
assert.Contains(t, schema.Properties, "owner")
assert.Contains(t, schema.Properties, "repo")
assert.Contains(t, schema.Properties, "pullNumber")
// `after` is required for cursor-based pagination on get_review_comments
// to be reachable from MCP clients; without it in the schema, callers
// cannot advance past the first page (issue #2122).
assert.Contains(t, schema.Properties, "after")
assert.Equal(t, "string", schema.Properties["after"].Type)
assert.ElementsMatch(t, schema.Required, []string{"method", "owner", "repo", "pullNumber"})

tests := []struct {
Expand Down Expand Up @@ -1804,6 +1809,54 @@ func Test_GetPullRequestComments(t *testing.T) {
assert.Equal(t, 1, result.TotalCount)
},
},
{
name: "after cursor is forwarded to GraphQL query",
gqlHTTPClient: githubv4mock.NewMockedHTTPClient(
githubv4mock.NewQueryMatcher(
reviewThreadsQuery{},
map[string]any{
"owner": githubv4.String("owner"),
"repo": githubv4.String("repo"),
"prNum": githubv4.Int(42),
"first": githubv4.Int(30),
"commentsPerThread": githubv4.Int(100),
"after": githubv4.String("cursor-page-2"),
},
githubv4mock.DataResponse(map[string]any{
"repository": map[string]any{
"pullRequest": map[string]any{
"reviewThreads": map[string]any{
"nodes": []map[string]any{},
"pageInfo": map[string]any{
"hasNextPage": false,
"hasPreviousPage": true,
"startCursor": "cursor3",
"endCursor": "cursor4",
},
"totalCount": 5,
},
},
},
}),
),
),
requestArgs: map[string]any{
"method": "get_review_comments",
"owner": "owner",
"repo": "repo",
"pullNumber": float64(42),
"after": "cursor-page-2",
},
expectError: false,
validateResult: func(t *testing.T, textContent string) {
var result MinimalReviewThreadsResponse
err := json.Unmarshal([]byte(textContent), &result)
require.NoError(t, err)
assert.Len(t, result.ReviewThreads, 0)
assert.Equal(t, true, result.PageInfo.HasPreviousPage)
assert.Equal(t, "cursor4", result.PageInfo.EndCursor)
},
},
{
name: "review threads fetch fails",
gqlHTTPClient: githubv4mock.NewMockedHTTPClient(
Expand Down