@@ -369,3 +369,196 @@ func TestTags(t *testing.T) {
369369 require .Equal (t , "v0.0.1" , trimmedTag [0 ].Name , "expected tag name to match" )
370370 require .Equal (t , * ref .Object .SHA , trimmedTag [0 ].Commit .SHA , "expected tag SHA to match" )
371371}
372+
373+ func TestPullRequestReview (t * testing.T ) {
374+ t .Parallel ()
375+
376+ mcpClient := setupMCPClient (t )
377+
378+ ctx := context .Background ()
379+
380+ // First, who am I
381+ getMeRequest := mcp.CallToolRequest {}
382+ getMeRequest .Params .Name = "get_me"
383+
384+ t .Log ("Getting current user..." )
385+ resp , err := mcpClient .CallTool (ctx , getMeRequest )
386+ require .NoError (t , err , "expected to call 'get_me' tool successfully" )
387+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
388+
389+ require .False (t , resp .IsError , "expected result not to be an error" )
390+ require .Len (t , resp .Content , 1 , "expected content to have one item" )
391+
392+ textContent , ok := resp .Content [0 ].(mcp.TextContent )
393+ require .True (t , ok , "expected content to be of type TextContent" )
394+
395+ var trimmedGetMeText struct {
396+ Login string `json:"login"`
397+ }
398+ err = json .Unmarshal ([]byte (textContent .Text ), & trimmedGetMeText )
399+ require .NoError (t , err , "expected to unmarshal text content successfully" )
400+
401+ currentOwner := trimmedGetMeText .Login
402+
403+ // Then create a repository with a README (via autoInit)
404+ repoName := fmt .Sprintf ("github-mcp-server-e2e-%s-%d" , t .Name (), time .Now ().UnixMilli ())
405+ createRepoRequest := mcp.CallToolRequest {}
406+ createRepoRequest .Params .Name = "create_repository"
407+ createRepoRequest .Params .Arguments = map [string ]any {
408+ "name" : repoName ,
409+ "private" : true ,
410+ "autoInit" : true ,
411+ }
412+
413+ t .Logf ("Creating repository %s/%s..." , currentOwner , repoName )
414+ _ , err = mcpClient .CallTool (ctx , createRepoRequest )
415+ require .NoError (t , err , "expected to call 'get_me' tool successfully" )
416+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
417+
418+ // Cleanup the repository after the test
419+ t .Cleanup (func () {
420+ // MCP Server doesn't support deletions, but we can use the GitHub Client
421+ ghClient := gogithub .NewClient (nil ).WithAuthToken (getE2EToken (t ))
422+ t .Logf ("Deleting repository %s/%s..." , currentOwner , repoName )
423+ _ , err := ghClient .Repositories .Delete (context .Background (), currentOwner , repoName )
424+ require .NoError (t , err , "expected to delete repository successfully" )
425+ })
426+
427+ // Create a branch on which to create a new commit
428+ createBranchRequest := mcp.CallToolRequest {}
429+ createBranchRequest .Params .Name = "create_branch"
430+ createBranchRequest .Params .Arguments = map [string ]any {
431+ "owner" : currentOwner ,
432+ "repo" : repoName ,
433+ "branch" : "test-branch" ,
434+ "from_branch" : "main" ,
435+ }
436+
437+ t .Logf ("Creating branch in %s/%s..." , currentOwner , repoName )
438+ resp , err = mcpClient .CallTool (ctx , createBranchRequest )
439+ require .NoError (t , err , "expected to call 'create_branch' tool successfully" )
440+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
441+
442+ // Create a commit with a new file
443+ commitRequest := mcp.CallToolRequest {}
444+ commitRequest .Params .Name = "create_or_update_file"
445+ commitRequest .Params .Arguments = map [string ]any {
446+ "owner" : currentOwner ,
447+ "repo" : repoName ,
448+ "path" : "test-file.txt" ,
449+ "content" : fmt .Sprintf ("Created by e2e test %s" , t .Name ()),
450+ "message" : "Add test file" ,
451+ "branch" : "test-branch" ,
452+ }
453+
454+ t .Logf ("Creating commit with new file in %s/%s..." , currentOwner , repoName )
455+ resp , err = mcpClient .CallTool (ctx , commitRequest )
456+ require .NoError (t , err , "expected to call 'create_or_update_file' tool successfully" )
457+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
458+
459+ textContent , ok = resp .Content [0 ].(mcp.TextContent )
460+ require .True (t , ok , "expected content to be of type TextContent" )
461+
462+ var trimmedCommitText struct {
463+ SHA string `json:"sha"`
464+ }
465+ err = json .Unmarshal ([]byte (textContent .Text ), & trimmedCommitText )
466+ require .NoError (t , err , "expected to unmarshal text content successfully" )
467+ commitId := trimmedCommitText .SHA
468+
469+ // Create a pull request
470+ prRequest := mcp.CallToolRequest {}
471+ prRequest .Params .Name = "create_pull_request"
472+ prRequest .Params .Arguments = map [string ]any {
473+ "owner" : currentOwner ,
474+ "repo" : repoName ,
475+ "title" : "Test PR" ,
476+ "body" : "This is a test PR" ,
477+ "head" : "test-branch" ,
478+ "base" : "main" ,
479+ "commitId" : commitId ,
480+ }
481+
482+ t .Logf ("Creating pull request in %s/%s..." , currentOwner , repoName )
483+ resp , err = mcpClient .CallTool (ctx , prRequest )
484+ require .NoError (t , err , "expected to call 'create_pull_request' tool successfully" )
485+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
486+
487+ // Create a review for the pull request, but we can't approve it
488+ // because the current owner also owns the PR.
489+ createPendingPullRequestReviewRequest := mcp.CallToolRequest {}
490+ createPendingPullRequestReviewRequest .Params .Name = "mvp_create_pending_pull_request_review"
491+ createPendingPullRequestReviewRequest .Params .Arguments = map [string ]any {
492+ "owner" : currentOwner ,
493+ "repo" : repoName ,
494+ "pullNumber" : 1 ,
495+ }
496+
497+ t .Logf ("Creating pending review for pull request in %s/%s..." , currentOwner , repoName )
498+ resp , err = mcpClient .CallTool (ctx , createPendingPullRequestReviewRequest )
499+ require .NoError (t , err , "expected to call 'mvp_create_pending_pull_request_review' tool successfully" )
500+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
501+
502+ textContent , ok = resp .Content [0 ].(mcp.TextContent )
503+ require .True (t , ok , "expected content to be of type TextContent" )
504+ require .Equal (t , "" , textContent .Text , "expected content to be empty" )
505+
506+ // Add a review comment
507+ addReviewCommentRequest := mcp.CallToolRequest {}
508+ addReviewCommentRequest .Params .Name = "mvp_add_pull_request_review_comment_to_pending_review"
509+ addReviewCommentRequest .Params .Arguments = map [string ]any {
510+ "owner" : currentOwner ,
511+ "repo" : repoName ,
512+ "pullNumber" : 1 ,
513+ "path" : "test-file.txt" ,
514+ "body" : "Very nice!" ,
515+ "line" : 1 ,
516+ }
517+
518+ t .Logf ("Adding review comment to pull request in %s/%s..." , currentOwner , repoName )
519+ resp , err = mcpClient .CallTool (ctx , addReviewCommentRequest )
520+ require .NoError (t , err , "expected to call 'mvp_add_pull_request_review_comment_to_pending_review' tool successfully" )
521+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
522+
523+ // Submit the review
524+ submitReviewRequest := mcp.CallToolRequest {}
525+ submitReviewRequest .Params .Name = "mvp_submit_pull_request_review"
526+ submitReviewRequest .Params .Arguments = map [string ]any {
527+ "owner" : currentOwner ,
528+ "repo" : repoName ,
529+ "pullNumber" : 1 ,
530+ "event" : "COMMENT" , // the only event we can use as the creator of the PR
531+ "body" : "Looks good if you like bad code I guess!" ,
532+ }
533+
534+ t .Logf ("Submitting review for pull request in %s/%s..." , currentOwner , repoName )
535+ resp , err = mcpClient .CallTool (ctx , submitReviewRequest )
536+ require .NoError (t , err , "expected to call 'mvp_submit_pull_request_review' tool successfully" )
537+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
538+
539+ // Finally, get the review and see that it has been created
540+ getPullRequestsReview := mcp.CallToolRequest {}
541+ getPullRequestsReview .Params .Name = "get_pull_request_reviews"
542+ getPullRequestsReview .Params .Arguments = map [string ]any {
543+ "owner" : currentOwner ,
544+ "repo" : repoName ,
545+ "pullNumber" : 1 ,
546+ }
547+
548+ t .Logf ("Getting reviews for pull request in %s/%s..." , currentOwner , repoName )
549+ resp , err = mcpClient .CallTool (ctx , getPullRequestsReview )
550+ require .NoError (t , err , "expected to call 'get_pull_request_reviews' tool successfully" )
551+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
552+
553+ textContent , ok = resp .Content [0 ].(mcp.TextContent )
554+ require .True (t , ok , "expected content to be of type TextContent" )
555+
556+ var reviews []struct {
557+ NodeID string `json:"node_id"`
558+ }
559+ err = json .Unmarshal ([]byte (textContent .Text ), & reviews )
560+ require .NoError (t , err , "expected to unmarshal text content successfully" )
561+
562+ // Check that there is one review
563+ require .Len (t , reviews , 1 , "expected to find one review" )
564+ }
0 commit comments