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