diff --git a/README.md b/README.md index b40974e20..1c79e0672 100644 --- a/README.md +++ b/README.md @@ -679,6 +679,7 @@ The following sets of tools are available (all are on by default): - `maintainer_can_modify`: Allow maintainer edits (boolean, optional) - `owner`: Repository owner (string, required) - `repo`: Repository name (string, required) + - `reviewers`: GitHub usernames to request reviews from (string[], optional) - `title`: PR title (string, required) - **delete_pending_pull_request_review** - Delete the requester's latest pending pull request review diff --git a/pkg/github/__toolsnaps__/create_pull_request.snap b/pkg/github/__toolsnaps__/create_pull_request.snap index 44142a79e..85ba52ea2 100644 --- a/pkg/github/__toolsnaps__/create_pull_request.snap +++ b/pkg/github/__toolsnaps__/create_pull_request.snap @@ -34,6 +34,13 @@ "description": "Repository name", "type": "string" }, + "reviewers": { + "description": "GitHub usernames to request reviews from", + "items": { + "type": "string" + }, + "type": "array" + }, "title": { "description": "PR title", "type": "string" diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index f82117cad..5b48f4aca 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -120,6 +120,12 @@ func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu mcp.WithBoolean("maintainer_can_modify", mcp.Description("Allow maintainer edits"), ), + mcp.WithArray("reviewers", + mcp.Description("GitHub usernames to request reviews from"), + mcp.Items(map[string]interface{}{ + "type": "string", + }), + ), ), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { owner, err := RequiredParam[string](request, "owner") @@ -158,6 +164,12 @@ func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu return mcp.NewToolResultError(err.Error()), nil } + // Handle reviewers parameter + reviewers, err := OptionalStringArrayParam(request, "reviewers") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } + newPR := &github.NewPullRequest{ Title: github.Ptr(title), Head: github.Ptr(head), @@ -193,6 +205,46 @@ func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu return mcp.NewToolResultError(fmt.Sprintf("failed to create pull request: %s", string(body))), nil } + // Request reviewers if provided + if len(reviewers) > 0 { + reviewersRequest := github.ReviewersRequest{ + Reviewers: reviewers, + } + + _, reviewResp, err := client.PullRequests.RequestReviewers(ctx, owner, repo, *pr.Number, reviewersRequest) + if err != nil { + return ghErrors.NewGitHubAPIErrorResponse(ctx, + "failed to request reviewers", + reviewResp, + err, + ), nil + } + defer func() { + if reviewResp != nil && reviewResp.Body != nil { + _ = reviewResp.Body.Close() + } + }() + + if reviewResp.StatusCode != http.StatusCreated && reviewResp.StatusCode != http.StatusOK { + body, err := io.ReadAll(reviewResp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %w", err) + } + return mcp.NewToolResultError(fmt.Sprintf("failed to request reviewers: %s", string(body))), nil + } + + // Refresh PR data to include reviewers + pr, resp, err = client.PullRequests.Get(ctx, owner, repo, *pr.Number) + if err != nil { + return ghErrors.NewGitHubAPIErrorResponse(ctx, + "failed to get updated pull request", + resp, + err, + ), nil + } + defer func() { _ = resp.Body.Close() }() + } + r, err := json.Marshal(pr) if err != nil { return nil, fmt.Errorf("failed to marshal response: %w", err) diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index 3a99d9f46..ee4b0f478 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -1791,6 +1791,7 @@ func Test_CreatePullRequest(t *testing.T) { assert.Contains(t, tool.InputSchema.Properties, "base") assert.Contains(t, tool.InputSchema.Properties, "draft") assert.Contains(t, tool.InputSchema.Properties, "maintainer_can_modify") + assert.Contains(t, tool.InputSchema.Properties, "reviewers") assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo", "title", "head", "base"}) // Setup mock PR for success case @@ -1885,6 +1886,49 @@ func Test_CreatePullRequest(t *testing.T) { expectError: true, expectedErrMsg: "failed to create pull request", }, + { + name: "successful PR creation with reviewers", + mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.PostReposPullsByOwnerByRepo, + expectRequestBody(t, map[string]interface{}{ + "title": "Test PR with reviewers", + "body": "This PR has reviewers", + "head": "feature-branch", + "base": "main", + "draft": false, + "maintainer_can_modify": true, + }).andThen( + mockResponse(t, http.StatusCreated, mockPR), + ), + ), + mock.WithRequestMatchHandler( + mock.PostReposPullsRequestedReviewersByOwnerByRepoByPullNumber, + expectRequestBody(t, map[string]interface{}{ + "reviewers": []interface{}{"reviewer1", "reviewer2"}, + }).andThen( + mockResponse(t, http.StatusCreated, mockPR), + ), + ), + mock.WithRequestMatch( + mock.GetReposPullsByOwnerByRepoByPullNumber, + mockPR, + ), + ), + requestArgs: map[string]interface{}{ + "owner": "owner", + "repo": "repo", + "title": "Test PR with reviewers", + "body": "This PR has reviewers", + "head": "feature-branch", + "base": "main", + "draft": false, + "maintainer_can_modify": true, + "reviewers": []string{"reviewer1", "reviewer2"}, + }, + expectError: false, + expectedPR: mockPR, + }, } for _, tc := range tests {
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: