@@ -508,6 +508,156 @@ func getPullRequestComments(client *github.Client, t translations.TranslationHel
508
508
}
509
509
}
510
510
511
+ // addPullRequestReviewComment creates a tool to add a review comment to a pull request.
512
+ func addPullRequestReviewComment (client * github.Client , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
513
+ return mcp .NewTool ("add_pull_request_review_comment" ,
514
+ mcp .WithDescription (t ("TOOL_ADD_PULL_REQUEST_COMMENT_DESCRIPTION" , "Add a review comment to a pull request" )),
515
+ mcp .WithString ("owner" ,
516
+ mcp .Required (),
517
+ mcp .Description ("Repository owner" ),
518
+ ),
519
+ mcp .WithString ("repo" ,
520
+ mcp .Required (),
521
+ mcp .Description ("Repository name" ),
522
+ ),
523
+ mcp .WithNumber ("pull_number" ,
524
+ mcp .Required (),
525
+ mcp .Description ("Pull request number" ),
526
+ ),
527
+ mcp .WithString ("body" ,
528
+ mcp .Required (),
529
+ mcp .Description ("The text of the review comment" ),
530
+ ),
531
+ mcp .WithString ("commit_id" ,
532
+ mcp .Required (),
533
+ mcp .Description ("The SHA of the commit to comment on" ),
534
+ ),
535
+ mcp .WithString ("path" ,
536
+ mcp .Required (),
537
+ mcp .Description ("The relative path to the file that necessitates a comment" ),
538
+ ),
539
+ mcp .WithString ("subject_type" ,
540
+ mcp .Description ("The level at which the comment is targeted, 'line' or 'file'" ),
541
+ mcp .Enum ("line" , "file" ),
542
+ ),
543
+ mcp .WithNumber ("line" ,
544
+ mcp .Description ("The line of the blob in the pull request diff that the comment applies to. For multi-line comments, the last line of the range" ),
545
+ ),
546
+ mcp .WithString ("side" ,
547
+ mcp .Description ("The side of the diff to comment on. Can be LEFT or RIGHT" ),
548
+ mcp .Enum ("LEFT" , "RIGHT" ),
549
+ ),
550
+ mcp .WithNumber ("start_line" ,
551
+ mcp .Description ("For multi-line comments, the first line of the range that the comment applies to" ),
552
+ ),
553
+ mcp .WithString ("start_side" ,
554
+ mcp .Description ("For multi-line comments, the starting side of the diff that the comment applies to. Can be LEFT or RIGHT" ),
555
+ mcp .Enum ("LEFT" , "RIGHT" ),
556
+ ),
557
+ mcp .WithNumber ("in_reply_to" ,
558
+ mcp .Description ("The ID of the review comment to reply to. When specified, all parameters other than body are ignored" ),
559
+ ),
560
+ ),
561
+ func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
562
+ owner , err := requiredParam [string ](request , "owner" )
563
+ if err != nil {
564
+ return mcp .NewToolResultError (err .Error ()), nil
565
+ }
566
+ repo , err := requiredParam [string ](request , "repo" )
567
+ if err != nil {
568
+ return mcp .NewToolResultError (err .Error ()), nil
569
+ }
570
+ pullNumber , err := requiredInt (request , "pull_number" )
571
+ if err != nil {
572
+ return mcp .NewToolResultError (err .Error ()), nil
573
+ }
574
+ body , err := requiredParam [string ](request , "body" )
575
+ if err != nil {
576
+ return mcp .NewToolResultError (err .Error ()), nil
577
+ }
578
+ commitID , err := requiredParam [string ](request , "commit_id" )
579
+ if err != nil {
580
+ return mcp .NewToolResultError (err .Error ()), nil
581
+ }
582
+ path , err := requiredParam [string ](request , "path" )
583
+ if err != nil {
584
+ return mcp .NewToolResultError (err .Error ()), nil
585
+ }
586
+
587
+ comment := & github.PullRequestComment {
588
+ Body : github .Ptr (body ),
589
+ CommitID : github .Ptr (commitID ),
590
+ Path : github .Ptr (path ),
591
+ }
592
+
593
+ // Check for in_reply_to parameter which takes precedence
594
+ if replyToFloat , ok := request .Params .Arguments ["in_reply_to" ].(float64 ); ok {
595
+ comment .InReplyTo = github .Ptr (int64 (replyToFloat ))
596
+ } else {
597
+ // Handle subject_type parameter
598
+ subjectType , err := optionalParam [string ](request , "subject_type" )
599
+ if err != nil {
600
+ return mcp .NewToolResultError (err .Error ()), nil
601
+ }
602
+ if subjectType == "file" {
603
+ // When commenting on a file, no line/position fields are needed
604
+ } else {
605
+ // Handle line or position-based comments
606
+ line , lineExists := request .Params .Arguments ["line" ].(float64 )
607
+ startLine , startLineExists := request .Params .Arguments ["start_line" ].(float64 )
608
+ side , sideExists := request .Params .Arguments ["side" ].(string )
609
+ startSide , startSideExists := request .Params .Arguments ["start_side" ].(string )
610
+
611
+ if subjectType != "file" && ! lineExists {
612
+ return mcp .NewToolResultError ("line parameter is required unless using subject_type:file or in_reply_to" ), nil
613
+ }
614
+
615
+ if lineExists {
616
+ comment .Line = github .Ptr (int (line ))
617
+ }
618
+ if sideExists {
619
+ comment .Side = github .Ptr (side )
620
+ }
621
+ if startLineExists {
622
+ comment .StartLine = github .Ptr (int (startLine ))
623
+ }
624
+ if startSideExists {
625
+ comment .StartSide = github .Ptr (startSide )
626
+ }
627
+
628
+ // Validate multi-line comment parameters
629
+ if startLineExists && ! lineExists {
630
+ return mcp .NewToolResultError ("if start_line is provided, line must also be provided" ), nil
631
+ }
632
+ if startSideExists && ! sideExists {
633
+ return mcp .NewToolResultError ("if start_side is provided, side must also be provided" ), nil
634
+ }
635
+ }
636
+ }
637
+
638
+ createdComment , resp , err := client .PullRequests .CreateComment (ctx , owner , repo , pullNumber , comment )
639
+ if err != nil {
640
+ return nil , fmt .Errorf ("failed to create pull request comment: %w" , err )
641
+ }
642
+ defer func () { _ = resp .Body .Close () }()
643
+
644
+ if resp .StatusCode != http .StatusCreated {
645
+ body , err := io .ReadAll (resp .Body )
646
+ if err != nil {
647
+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
648
+ }
649
+ return mcp .NewToolResultError (fmt .Sprintf ("failed to create pull request comment: %s" , string (body ))), nil
650
+ }
651
+
652
+ r , err := json .Marshal (createdComment )
653
+ if err != nil {
654
+ return nil , fmt .Errorf ("failed to marshal response: %w" , err )
655
+ }
656
+
657
+ return mcp .NewToolResultText (string (r )), nil
658
+ }
659
+ }
660
+
511
661
// getPullRequestReviews creates a tool to get the reviews on a pull request.
512
662
func getPullRequestReviews (client * github.Client , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
513
663
return mcp .NewTool ("get_pull_request_reviews" ,
0 commit comments