Skip to content

Commit 7c277bf

Browse files
salakonradclaude
andcommitted
feat: add get_reviewers and get_status_checks methods to pull_request_read
Extends the pull_request_read tool with two new methods: - get_reviewers: returns pending reviewer requests (both individual users and teams) using the PullRequests.ListReviewers API. This fills a gap in the existing toolset — requested team reviewers were previously not exposed anywhere in the MCP server. - get_status_checks: returns a unified view of all status checks for a PR's head commit by combining legacy commit statuses (e.g. Atlantis plan/policy results) and modern GitHub Actions check runs into a single response with a top-level combined_state. New output types added to minimal_types.go: - MinimalReviewerUser, MinimalReviewerTeam, MinimalPRReviewers - MinimalCommitStatus, MinimalStatusChecks Adds unit tests for both methods (success and API-failure paths), regenerates the pull_request_read toolsnap, and updates the generated README tool documentation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8ac674b commit 7c277bf

7 files changed

Lines changed: 422 additions & 4 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,8 @@ The following sets of tools are available:
11891189
7. get_reviews - Get the reviews on a pull request. When asked for review comments, use get_review_comments method. Use with pagination parameters to control the number of results returned.
11901190
8. 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.
11911191
9. 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.
1192+
10. get_reviewers - Get the list of requested reviewers (users and teams) for a pull request who have not yet submitted a review.
1193+
11. get_status_checks - Get a unified view of all status checks for a pull request, combining legacy commit statuses and modern check runs.
11921194
(string, required)
11931195
- `owner`: Repository owner (string, required)
11941196
- `page`: Page number for pagination (min 1) (number, optional)

pkg/github/__toolsnaps__/pull_request_read.snap

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"type": "string"
1313
},
1414
"method": {
15-
"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_commits - Get the list of commits on a pull request. Use with pagination parameters to control the number of results returned.\n 6. 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 7. get_reviews - Get the reviews on a pull request. When asked for review comments, use get_review_comments method. Use with pagination parameters to control the number of results returned.\n 8. 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 9. 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",
15+
"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_commits - Get the list of commits on a pull request. Use with pagination parameters to control the number of results returned.\n 6. 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 7. get_reviews - Get the reviews on a pull request. When asked for review comments, use get_review_comments method. Use with pagination parameters to control the number of results returned.\n 8. 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 9. 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 10. get_reviewers - Get the list of requested reviewers (users and teams) for a pull request who have not yet submitted a review.\n 11. get_status_checks - Get a unified view of all status checks for a pull request, combining legacy commit statuses and modern check runs.\n",
1616
"enum": [
1717
"get",
1818
"get_diff",
@@ -22,7 +22,9 @@
2222
"get_review_comments",
2323
"get_reviews",
2424
"get_comments",
25-
"get_check_runs"
25+
"get_check_runs",
26+
"get_reviewers",
27+
"get_status_checks"
2628
],
2729
"type": "string"
2830
},

pkg/github/helper_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ const (
8282
PutReposPullsMergeByOwnerByRepoByPullNumber = "PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge"
8383
PutReposPullsUpdateBranchByOwnerByRepoByPullNumber = "PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch"
8484
PostReposPullsRequestedReviewersByOwnerByRepoByPullNumber = "POST /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"
85+
GetReposPullsRequestedReviewersByOwnerByRepoByPullNumber = "GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"
8586
PostReposPullsCommentsByOwnerByRepoByPullNumber = "POST /repos/{owner}/{repo}/pulls/{pull_number}/comments"
8687
PostReposPullsCommentsReactionsByOwnerByRepoByCommentID = "POST /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions"
8788

pkg/github/minimal_types.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,6 +1827,41 @@ type MinimalCheckRunsResult struct {
18271827
CheckRuns []MinimalCheckRun `json:"check_runs"`
18281828
}
18291829

1830+
// MinimalReviewerUser is a user requested to review a pull request.
1831+
type MinimalReviewerUser struct {
1832+
Login string `json:"login"`
1833+
HTMLURL string `json:"html_url,omitempty"`
1834+
}
1835+
1836+
// MinimalReviewerTeam is a team requested to review a pull request.
1837+
type MinimalReviewerTeam struct {
1838+
Slug string `json:"slug"`
1839+
Name string `json:"name,omitempty"`
1840+
HTMLURL string `json:"html_url,omitempty"`
1841+
}
1842+
1843+
// MinimalPRReviewers contains the list of requested reviewers for a pull request.
1844+
type MinimalPRReviewers struct {
1845+
Users []MinimalReviewerUser `json:"users"`
1846+
Teams []MinimalReviewerTeam `json:"teams"`
1847+
}
1848+
1849+
// MinimalCommitStatus represents a single commit status check.
1850+
type MinimalCommitStatus struct {
1851+
State string `json:"state"`
1852+
Context string `json:"context"`
1853+
Description string `json:"description,omitempty"`
1854+
TargetURL string `json:"target_url,omitempty"`
1855+
}
1856+
1857+
// MinimalStatusChecks contains a unified view of all status checks for a PR.
1858+
type MinimalStatusChecks struct {
1859+
CombinedState string `json:"combined_state"`
1860+
Statuses []MinimalCommitStatus `json:"statuses"`
1861+
CheckRuns []MinimalCheckRun `json:"check_runs"`
1862+
TotalCount int `json:"total_count"`
1863+
}
1864+
18301865
// convertToMinimalCheckRun converts a GitHub API CheckRun to MinimalCheckRun
18311866
func convertToMinimalCheckRun(checkRun *github.CheckRun) MinimalCheckRun {
18321867
minimalCheckRun := MinimalCheckRun{

pkg/github/projects_resolver_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ func Test_ResolveFieldNamesToIDs_Success(t *testing.T) {
358358
assert.Equal(t, []int64{100, 200}, ids)
359359
}
360360

361-
// Field and single-select option name matching is case-insensitive so agents passing lowercase
361+
// Field and single-select option name matching is case-insensitive so agents passing lowercase
362362
// names like "status" or "in progress" resolve to "Status" and "In Progress" respectively.
363363
func Test_ResolveProjectFieldByName_CaseInsensitive(t *testing.T) {
364364
mocked := githubv4mock.NewMockedHTTPClient(

pkg/github/pullrequests.go

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ Possible options:
4141
7. get_reviews - Get the reviews on a pull request. When asked for review comments, use get_review_comments method. Use with pagination parameters to control the number of results returned.
4242
8. 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.
4343
9. 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.
44+
10. get_reviewers - Get the list of requested reviewers (users and teams) for a pull request who have not yet submitted a review.
45+
11. get_status_checks - Get a unified view of all status checks for a pull request, combining legacy commit statuses and modern check runs.
4446
`,
45-
Enum: []any{"get", "get_diff", "get_status", "get_files", "get_commits", "get_review_comments", "get_reviews", "get_comments", "get_check_runs"},
47+
Enum: []any{"get", "get_diff", "get_status", "get_files", "get_commits", "get_review_comments", "get_reviews", "get_comments", "get_check_runs", "get_reviewers", "get_status_checks"},
4648
},
4749
"owner": {
4850
Type: "string",
@@ -154,6 +156,12 @@ Possible options:
154156
case "get_check_runs":
155157
result, err := GetPullRequestCheckRuns(ctx, client, owner, repo, pullNumber, pagination)
156158
return attachIFC(result), nil, err
159+
case "get_reviewers":
160+
result, err := GetPullRequestReviewers(ctx, client, owner, repo, pullNumber)
161+
return attachIFC(result), nil, err
162+
case "get_status_checks":
163+
result, err := GetPullRequestStatusChecks(ctx, client, owner, repo, pullNumber)
164+
return attachIFC(result), nil, err
157165
default:
158166
return utils.NewToolResultError(fmt.Sprintf("unknown method: %s", method)), nil, nil
159167
}
@@ -358,6 +366,117 @@ func GetPullRequestCheckRuns(ctx context.Context, client *github.Client, owner,
358366
return utils.NewToolResultText(string(r)), nil
359367
}
360368

369+
func GetPullRequestReviewers(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) {
370+
reviewers, resp, err := client.PullRequests.ListReviewers(ctx, owner, repo, pullNumber)
371+
if err != nil {
372+
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get pull request reviewers", resp, err), nil
373+
}
374+
defer func() { _ = resp.Body.Close() }()
375+
376+
if resp.StatusCode != http.StatusOK {
377+
body, err := io.ReadAll(resp.Body)
378+
if err != nil {
379+
return nil, fmt.Errorf("failed to read response body: %w", err)
380+
}
381+
return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to get pull request reviewers", resp, body), nil
382+
}
383+
384+
result := MinimalPRReviewers{}
385+
for _, u := range reviewers.Users {
386+
result.Users = append(result.Users, MinimalReviewerUser{
387+
Login: u.GetLogin(),
388+
HTMLURL: u.GetHTMLURL(),
389+
})
390+
}
391+
for _, t := range reviewers.Teams {
392+
result.Teams = append(result.Teams, MinimalReviewerTeam{
393+
Slug: t.GetSlug(),
394+
Name: t.GetName(),
395+
HTMLURL: t.GetHTMLURL(),
396+
})
397+
}
398+
399+
r, err := json.Marshal(result)
400+
if err != nil {
401+
return nil, fmt.Errorf("failed to marshal response: %w", err)
402+
}
403+
404+
return utils.NewToolResultText(string(r)), nil
405+
}
406+
407+
func GetPullRequestStatusChecks(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) {
408+
pr, resp, err := client.PullRequests.Get(ctx, owner, repo, pullNumber)
409+
if err != nil {
410+
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get pull request", resp, err), nil
411+
}
412+
defer func() { _ = resp.Body.Close() }()
413+
414+
if resp.StatusCode != http.StatusOK {
415+
body, err := io.ReadAll(resp.Body)
416+
if err != nil {
417+
return nil, fmt.Errorf("failed to read response body: %w", err)
418+
}
419+
return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to get pull request", resp, body), nil
420+
}
421+
422+
sha := pr.GetHead().GetSHA()
423+
424+
combinedStatus, resp, err := client.Repositories.GetCombinedStatus(ctx, owner, repo, sha, nil)
425+
if err != nil {
426+
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get combined status", resp, err), nil
427+
}
428+
defer func() { _ = resp.Body.Close() }()
429+
430+
if resp.StatusCode != http.StatusOK {
431+
body, err := io.ReadAll(resp.Body)
432+
if err != nil {
433+
return nil, fmt.Errorf("failed to read response body: %w", err)
434+
}
435+
return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to get combined status", resp, body), nil
436+
}
437+
438+
checkRuns, resp, err := client.Checks.ListCheckRunsForRef(ctx, owner, repo, sha, nil)
439+
if err != nil {
440+
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get check runs", resp, err), nil
441+
}
442+
defer func() { _ = resp.Body.Close() }()
443+
444+
if resp.StatusCode != http.StatusOK {
445+
body, err := io.ReadAll(resp.Body)
446+
if err != nil {
447+
return nil, fmt.Errorf("failed to read response body: %w", err)
448+
}
449+
return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to get check runs", resp, body), nil
450+
}
451+
452+
result := MinimalStatusChecks{
453+
CombinedState: combinedStatus.GetState(),
454+
}
455+
456+
for _, s := range combinedStatus.Statuses {
457+
ms := MinimalCommitStatus{
458+
State: s.GetState(),
459+
Context: s.GetContext(),
460+
Description: s.GetDescription(),
461+
TargetURL: s.GetTargetURL(),
462+
}
463+
result.Statuses = append(result.Statuses, ms)
464+
}
465+
466+
for _, cr := range checkRuns.CheckRuns {
467+
result.CheckRuns = append(result.CheckRuns, convertToMinimalCheckRun(cr))
468+
}
469+
470+
result.TotalCount = len(result.Statuses) + len(result.CheckRuns)
471+
472+
r, err := json.Marshal(result)
473+
if err != nil {
474+
return nil, fmt.Errorf("failed to marshal response: %w", err)
475+
}
476+
477+
return utils.NewToolResultText(string(r)), nil
478+
}
479+
361480
func GetPullRequestFiles(ctx context.Context, client *github.Client, owner, repo string, pullNumber int, pagination PaginationParams) (*mcp.CallToolResult, error) {
362481
opts := &github.ListOptions{
363482
PerPage: pagination.PerPage,

0 commit comments

Comments
 (0)