@@ -499,6 +499,156 @@ func GetPullRequestComments(client *github.Client, t translations.TranslationHel
499499 }
500500}
501501
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+
502652// GetPullRequestReviews creates a tool to get the reviews on a pull request.
503653func GetPullRequestReviews (client * github.Client , t translations.TranslationHelperFunc ) (tool mcp.Tool , handler server.ToolHandlerFunc ) {
504654 return mcp .NewTool ("get_pull_request_reviews" ,
0 commit comments