Skip to content

Commit ce6df32

Browse files
ashwin-antclaude
andcommitted
Unify PR review comment tools into a single consolidated tool
The separate AddPullRequestReviewComment and ReplyToPullRequestReviewComment tools have been merged into a single tool that handles both creating new comments and replying to existing ones. This approach simplifies the API and provides a more consistent interface for users. - Made commit_id and path optional when using in_reply_to for replies - Updated the tests to verify both comment and reply functionality - Removed the separate ReplyToPullRequestReviewComment tool - Fixed test expectations to match how errors are returned 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bab5c0e commit ce6df32

File tree

3 files changed

+128
-215
lines changed

3 files changed

+128
-215
lines changed

pkg/github/pullrequests.go

Lines changed: 62 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -665,12 +665,10 @@ func AddPullRequestReviewComment(getClient GetClientFn, t translations.Translati
665665
mcp.Description("The text of the review comment"),
666666
),
667667
mcp.WithString("commit_id",
668-
mcp.Required(),
669-
mcp.Description("The SHA of the commit to comment on"),
668+
mcp.Description("The SHA of the commit to comment on. Required unless in_reply_to is specified."),
670669
),
671670
mcp.WithString("path",
672-
mcp.Required(),
673-
mcp.Description("The relative path to the file that necessitates a comment"),
671+
mcp.Description("The relative path to the file that necessitates a comment. Required unless in_reply_to is specified."),
674672
),
675673
mcp.WithString("subject_type",
676674
mcp.Description("The level at which the comment is targeted, 'line' or 'file'"),
@@ -691,7 +689,7 @@ func AddPullRequestReviewComment(getClient GetClientFn, t translations.Translati
691689
mcp.Enum("LEFT", "RIGHT"),
692690
),
693691
mcp.WithNumber("in_reply_to",
694-
mcp.Description("The ID of the review comment to reply to. When specified, all parameters other than body are ignored"),
692+
mcp.Description("The ID of the review comment to reply to. When specified, only body is required and all other parameters are ignored"),
695693
),
696694
),
697695
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -711,161 +709,103 @@ func AddPullRequestReviewComment(getClient GetClientFn, t translations.Translati
711709
if err != nil {
712710
return mcp.NewToolResultError(err.Error()), nil
713711
}
714-
commitID, err := requiredParam[string](request, "commit_id")
715-
if err != nil {
716-
return mcp.NewToolResultError(err.Error()), nil
717-
}
718-
path, err := requiredParam[string](request, "path")
719-
if err != nil {
720-
return mcp.NewToolResultError(err.Error()), nil
721-
}
722712

723-
comment := &github.PullRequestComment{
724-
Body: github.Ptr(body),
725-
CommitID: github.Ptr(commitID),
726-
Path: github.Ptr(path),
713+
client, err := getClient(ctx)
714+
if err != nil {
715+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
727716
}
728717

729-
// Check for in_reply_to parameter which takes precedence
718+
// Check if this is a reply to an existing comment
730719
if replyToFloat, ok := request.Params.Arguments["in_reply_to"].(float64); ok {
731-
comment.InReplyTo = github.Ptr(int64(replyToFloat))
732-
} else {
733-
// Handle subject_type parameter
734-
subjectType, err := OptionalParam[string](request, "subject_type")
720+
// Use the specialized method for reply comments due to inconsistency in underlying go-github library: https://github.com/google/go-github/pull/950
721+
commentID := int64(replyToFloat)
722+
createdReply, resp, err := client.PullRequests.CreateCommentInReplyTo(ctx, owner, repo, pullNumber, body, commentID)
735723
if err != nil {
736-
return mcp.NewToolResultError(err.Error()), nil
724+
return nil, fmt.Errorf("failed to reply to pull request comment: %w", err)
737725
}
738-
if subjectType != "file" {
739-
// Handle line or position-based comments
740-
// No action needed if subjectType is "file"
741-
line, lineExists := request.Params.Arguments["line"].(float64)
742-
startLine, startLineExists := request.Params.Arguments["start_line"].(float64)
743-
side, sideExists := request.Params.Arguments["side"].(string)
744-
startSide, startSideExists := request.Params.Arguments["start_side"].(string)
745-
746-
if subjectType != "file" && !lineExists {
747-
return mcp.NewToolResultError("line parameter is required unless using subject_type:file or in_reply_to"), nil
748-
}
726+
defer func() { _ = resp.Body.Close() }()
749727

750-
if lineExists {
751-
comment.Line = github.Ptr(int(line))
752-
}
753-
if sideExists {
754-
comment.Side = github.Ptr(side)
755-
}
756-
if startLineExists {
757-
comment.StartLine = github.Ptr(int(startLine))
758-
}
759-
if startSideExists {
760-
comment.StartSide = github.Ptr(startSide)
761-
}
762-
763-
// Validate multi-line comment parameters
764-
if startLineExists && !lineExists {
765-
return mcp.NewToolResultError("if start_line is provided, line must also be provided"), nil
766-
}
767-
if startSideExists && !sideExists {
768-
return mcp.NewToolResultError("if start_side is provided, side must also be provided"), nil
728+
if resp.StatusCode != http.StatusCreated {
729+
respBody, err := io.ReadAll(resp.Body)
730+
if err != nil {
731+
return nil, fmt.Errorf("failed to read response body: %w", err)
769732
}
733+
return mcp.NewToolResultError(fmt.Sprintf("failed to reply to pull request comment: %s", string(respBody))), nil
770734
}
771-
}
772-
773-
client, err := getClient(ctx)
774-
if err != nil {
775-
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
776-
}
777-
778-
createdComment, resp, err := client.PullRequests.CreateComment(ctx, owner, repo, pullNumber, comment)
779-
if err != nil {
780-
return nil, fmt.Errorf("failed to create pull request comment: %w", err)
781-
}
782-
defer func() { _ = resp.Body.Close() }()
783735

784-
if resp.StatusCode != http.StatusCreated {
785-
body, err := io.ReadAll(resp.Body)
736+
r, err := json.Marshal(createdReply)
786737
if err != nil {
787-
return nil, fmt.Errorf("failed to read response body: %w", err)
738+
return nil, fmt.Errorf("failed to marshal response: %w", err)
788739
}
789-
return mcp.NewToolResultError(fmt.Sprintf("failed to create pull request comment: %s", string(body))), nil
790-
}
791740

792-
r, err := json.Marshal(createdComment)
793-
if err != nil {
794-
return nil, fmt.Errorf("failed to marshal response: %w", err)
741+
return mcp.NewToolResultText(string(r)), nil
795742
}
796743

797-
return mcp.NewToolResultText(string(r)), nil
798-
}
799-
}
800-
801-
// ReplyToPullRequestReviewComment creates a tool to reply to an existing review comment on a pull request.
802-
func ReplyToPullRequestReviewComment(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool,
803-
handler server.ToolHandlerFunc) {
804-
return mcp.NewTool("reply_to_pull_request_review_comment",
805-
mcp.WithDescription(t("TOOL_REPLY_TO_PULL_REQUEST_REVIEW_COMMENT_DESCRIPTION", "Reply to an existing review comment on a pull request")),
806-
mcp.WithString("owner",
807-
mcp.Required(),
808-
mcp.Description("Repository owner"),
809-
),
810-
mcp.WithString("repo",
811-
mcp.Required(),
812-
mcp.Description("Repository name"),
813-
),
814-
mcp.WithNumber("pull_number",
815-
mcp.Required(),
816-
mcp.Description("Pull request number"),
817-
),
818-
mcp.WithNumber("comment_id",
819-
mcp.Required(),
820-
mcp.Description("The unique identifier of the comment to reply to"),
821-
),
822-
mcp.WithString("body",
823-
mcp.Required(),
824-
mcp.Description("The text of the reply comment"),
825-
),
826-
),
827-
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
828-
owner, err := requiredParam[string](request, "owner")
829-
if err != nil {
830-
return mcp.NewToolResultError(err.Error()), nil
831-
}
832-
repo, err := requiredParam[string](request, "repo")
744+
// This is a new comment, not a reply
745+
// Verify required parameters for a new comment
746+
commitID, err := requiredParam[string](request, "commit_id")
833747
if err != nil {
834748
return mcp.NewToolResultError(err.Error()), nil
835749
}
836-
pullNumber, err := RequiredInt(request, "pull_number")
750+
path, err := requiredParam[string](request, "path")
837751
if err != nil {
838752
return mcp.NewToolResultError(err.Error()), nil
839753
}
840-
commentID, err := RequiredInt(request, "comment_id")
841-
if err != nil {
842-
return mcp.NewToolResultError(err.Error()), nil
754+
755+
comment := &github.PullRequestComment{
756+
Body: github.Ptr(body),
757+
CommitID: github.Ptr(commitID),
758+
Path: github.Ptr(path),
843759
}
844-
body, err := requiredParam[string](request, "body")
760+
761+
subjectType, err := OptionalParam[string](request, "subject_type")
845762
if err != nil {
846763
return mcp.NewToolResultError(err.Error()), nil
847764
}
765+
if subjectType != "file" {
766+
line, lineExists := request.Params.Arguments["line"].(float64)
767+
startLine, startLineExists := request.Params.Arguments["start_line"].(float64)
768+
side, sideExists := request.Params.Arguments["side"].(string)
769+
startSide, startSideExists := request.Params.Arguments["start_side"].(string)
848770

849-
client, err := getClient(ctx)
850-
if err != nil {
851-
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
771+
if !lineExists {
772+
return mcp.NewToolResultError("line parameter is required unless using subject_type:file"), nil
773+
}
774+
775+
comment.Line = github.Ptr(int(line))
776+
if sideExists {
777+
comment.Side = github.Ptr(side)
778+
}
779+
if startLineExists {
780+
comment.StartLine = github.Ptr(int(startLine))
781+
}
782+
if startSideExists {
783+
comment.StartSide = github.Ptr(startSide)
784+
}
785+
786+
if startLineExists && !lineExists {
787+
return mcp.NewToolResultError("if start_line is provided, line must also be provided"), nil
788+
}
789+
if startSideExists && !sideExists {
790+
return mcp.NewToolResultError("if start_side is provided, side must also be provided"), nil
791+
}
852792
}
853793

854-
createdReply, resp, err := client.PullRequests.CreateCommentInReplyTo(ctx, owner, repo, pullNumber, body, int64(commentID))
794+
createdComment, resp, err := client.PullRequests.CreateComment(ctx, owner, repo, pullNumber, comment)
855795
if err != nil {
856-
return nil, fmt.Errorf("failed to reply to pull request comment: %w", err)
796+
return nil, fmt.Errorf("failed to create pull request comment: %w", err)
857797
}
858798
defer func() { _ = resp.Body.Close() }()
859799

860800
if resp.StatusCode != http.StatusCreated {
861-
body, err := io.ReadAll(resp.Body)
801+
respBody, err := io.ReadAll(resp.Body)
862802
if err != nil {
863803
return nil, fmt.Errorf("failed to read response body: %w", err)
864804
}
865-
return mcp.NewToolResultError(fmt.Sprintf("failed to reply to pull request comment: %s", string(body))), nil
805+
return mcp.NewToolResultError(fmt.Sprintf("failed to create pull request comment: %s", string(respBody))), nil
866806
}
867807

868-
r, err := json.Marshal(createdReply)
808+
r, err := json.Marshal(createdComment)
869809
if err != nil {
870810
return nil, fmt.Errorf("failed to marshal response: %w", err)
871811
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy