@@ -206,3 +206,199 @@ func TestToolsets(t *testing.T) {
206
206
require .True (t , toolsContains ("list_branches" ), "expected to find 'list_branches' tool" )
207
207
require .False (t , toolsContains ("get_pull_request" ), "expected not to find 'get_pull_request' tool" )
208
208
}
209
+
210
+ func TestPullRequestReview (t * testing.T ) {
211
+ t .Parallel ()
212
+
213
+ mcpClient := setupMCPClient (t )
214
+
215
+ ctx := context .Background ()
216
+
217
+ // First, who am I
218
+ getMeRequest := mcp.CallToolRequest {}
219
+ getMeRequest .Params .Name = "get_me"
220
+
221
+ t .Log ("Getting current user..." )
222
+ resp , err := mcpClient .CallTool (ctx , getMeRequest )
223
+ require .NoError (t , err , "expected to call 'get_me' tool successfully" )
224
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
225
+
226
+ require .False (t , resp .IsError , "expected result not to be an error" )
227
+ require .Len (t , resp .Content , 1 , "expected content to have one item" )
228
+
229
+ textContent , ok := resp .Content [0 ].(mcp.TextContent )
230
+ require .True (t , ok , "expected content to be of type TextContent" )
231
+
232
+ var trimmedGetMeText struct {
233
+ Login string `json:"login"`
234
+ }
235
+ err = json .Unmarshal ([]byte (textContent .Text ), & trimmedGetMeText )
236
+ require .NoError (t , err , "expected to unmarshal text content successfully" )
237
+
238
+ currentOwner := trimmedGetMeText .Login
239
+
240
+ // Then create a repository with a README (via autoInit)
241
+ repoName := fmt .Sprintf ("github-mcp-server-e2e-%s-%d" , t .Name (), time .Now ().UnixMilli ())
242
+ createRepoRequest := mcp.CallToolRequest {}
243
+ createRepoRequest .Params .Name = "create_repository"
244
+ createRepoRequest .Params .Arguments = map [string ]any {
245
+ "name" : repoName ,
246
+ "private" : true ,
247
+ "autoInit" : true ,
248
+ }
249
+
250
+ t .Logf ("Creating repository %s/%s..." , currentOwner , repoName )
251
+ _ , err = mcpClient .CallTool (ctx , createRepoRequest )
252
+ require .NoError (t , err , "expected to call 'get_me' tool successfully" )
253
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
254
+
255
+ // Cleanup the repository after the test
256
+ t .Cleanup (func () {
257
+ // MCP Server doesn't support deletions, but we can use the GitHub Client
258
+ ghClient := github .NewClient (nil ).WithAuthToken (getE2EToken (t ))
259
+ t .Logf ("Deleting repository %s/%s..." , currentOwner , repoName )
260
+ _ , err := ghClient .Repositories .Delete (context .Background (), currentOwner , repoName )
261
+ require .NoError (t , err , "expected to delete repository successfully" )
262
+ })
263
+
264
+ // Create a branch on which to create a new commit
265
+ createBranchRequest := mcp.CallToolRequest {}
266
+ createBranchRequest .Params .Name = "create_branch"
267
+ createBranchRequest .Params .Arguments = map [string ]any {
268
+ "owner" : currentOwner ,
269
+ "repo" : repoName ,
270
+ "branch" : "test-branch" ,
271
+ "from_branch" : "main" ,
272
+ }
273
+
274
+ t .Logf ("Creating branch in %s/%s..." , currentOwner , repoName )
275
+ resp , err = mcpClient .CallTool (ctx , createBranchRequest )
276
+ require .NoError (t , err , "expected to call 'create_branch' tool successfully" )
277
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
278
+
279
+ // Create a commit with a new file
280
+ commitRequest := mcp.CallToolRequest {}
281
+ commitRequest .Params .Name = "create_or_update_file"
282
+ commitRequest .Params .Arguments = map [string ]any {
283
+ "owner" : currentOwner ,
284
+ "repo" : repoName ,
285
+ "path" : "test-file.txt" ,
286
+ "content" : fmt .Sprintf ("Created by e2e test %s" , t .Name ()),
287
+ "message" : "Add test file" ,
288
+ "branch" : "test-branch" ,
289
+ }
290
+
291
+ t .Logf ("Creating commit with new file in %s/%s..." , currentOwner , repoName )
292
+ resp , err = mcpClient .CallTool (ctx , commitRequest )
293
+ require .NoError (t , err , "expected to call 'create_or_update_file' tool successfully" )
294
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
295
+
296
+ textContent , ok = resp .Content [0 ].(mcp.TextContent )
297
+ require .True (t , ok , "expected content to be of type TextContent" )
298
+
299
+ var trimmedCommitText struct {
300
+ SHA string `json:"sha"`
301
+ }
302
+ err = json .Unmarshal ([]byte (textContent .Text ), & trimmedCommitText )
303
+ require .NoError (t , err , "expected to unmarshal text content successfully" )
304
+ commitId := trimmedCommitText .SHA
305
+
306
+ // Create a pull request
307
+ prRequest := mcp.CallToolRequest {}
308
+ prRequest .Params .Name = "create_pull_request"
309
+ prRequest .Params .Arguments = map [string ]any {
310
+ "owner" : currentOwner ,
311
+ "repo" : repoName ,
312
+ "title" : "Test PR" ,
313
+ "body" : "This is a test PR" ,
314
+ "head" : "test-branch" ,
315
+ "base" : "main" ,
316
+ "commitId" : commitId ,
317
+ }
318
+
319
+ t .Logf ("Creating pull request in %s/%s..." , currentOwner , repoName )
320
+ resp , err = mcpClient .CallTool (ctx , prRequest )
321
+ require .NoError (t , err , "expected to call 'create_pull_request' tool successfully" )
322
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
323
+
324
+ // Create a review for the pull request, but we can't approve it
325
+ // because the current owner also owns the PR.
326
+ createPendingPullRequestReviewRequest := mcp.CallToolRequest {}
327
+ createPendingPullRequestReviewRequest .Params .Name = "mvp_create_pending_pull_request_review"
328
+ createPendingPullRequestReviewRequest .Params .Arguments = map [string ]any {
329
+ "owner" : currentOwner ,
330
+ "repo" : repoName ,
331
+ "pullNumber" : 1 ,
332
+ }
333
+
334
+ t .Logf ("Creating pending review for pull request in %s/%s..." , currentOwner , repoName )
335
+ resp , err = mcpClient .CallTool (ctx , createPendingPullRequestReviewRequest )
336
+ require .NoError (t , err , "expected to call 'mvp_create_pending_pull_request_review' tool successfully" )
337
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
338
+
339
+ textContent , ok = resp .Content [0 ].(mcp.TextContent )
340
+ require .True (t , ok , "expected content to be of type TextContent" )
341
+
342
+ var trimmedReviewRequestResponse struct {
343
+ PullRequestReviewID string `json:"pullRequestReviewID"`
344
+ }
345
+ err = json .Unmarshal ([]byte (textContent .Text ), & trimmedReviewRequestResponse )
346
+ require .NoError (t , err , "expected to unmarshal text content successfully" )
347
+ pullRequestReviewId := trimmedReviewRequestResponse .PullRequestReviewID
348
+
349
+ // Add a review comment
350
+ addReviewCommentRequest := mcp.CallToolRequest {}
351
+ addReviewCommentRequest .Params .Name = "mvp_add_pull_request_review_comment"
352
+ addReviewCommentRequest .Params .Arguments = map [string ]any {
353
+ "path" : "test-file.txt" ,
354
+ "body" : "Very nice!" ,
355
+ "line" : 1 ,
356
+ "pullRequestReviewID" : pullRequestReviewId ,
357
+ }
358
+
359
+ t .Logf ("Adding review comment to pull request in %s/%s..." , currentOwner , repoName )
360
+ resp , err = mcpClient .CallTool (ctx , addReviewCommentRequest )
361
+ require .NoError (t , err , "expected to call 'add_pull_request_review_comment' tool successfully" )
362
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
363
+
364
+ // Submit the review
365
+ submitReviewRequest := mcp.CallToolRequest {}
366
+ submitReviewRequest .Params .Name = "mvp_submit_pull_request_review"
367
+ submitReviewRequest .Params .Arguments = map [string ]any {
368
+ "event" : "COMMENT" , // the only event we can use as the creator of the PR
369
+ "body" : "Needs improvement!" ,
370
+ "pullRequestReviewID" : pullRequestReviewId ,
371
+ }
372
+
373
+ t .Logf ("Submitting review for pull request in %s/%s..." , currentOwner , repoName )
374
+ resp , err = mcpClient .CallTool (ctx , submitReviewRequest )
375
+ require .NoError (t , err , "expected to call 'mvp_submit_pull_request_review' tool successfully" )
376
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
377
+
378
+ // Finally, get the review and see that it has been created
379
+ getPullRequestsReview := mcp.CallToolRequest {}
380
+ getPullRequestsReview .Params .Name = "get_pull_request_reviews"
381
+ getPullRequestsReview .Params .Arguments = map [string ]any {
382
+ "owner" : currentOwner ,
383
+ "repo" : repoName ,
384
+ "pullNumber" : 1 ,
385
+ }
386
+
387
+ t .Logf ("Getting reviews for pull request in %s/%s..." , currentOwner , repoName )
388
+ resp , err = mcpClient .CallTool (ctx , getPullRequestsReview )
389
+ require .NoError (t , err , "expected to call 'get_pull_request_reviews' tool successfully" )
390
+ require .False (t , resp .IsError , fmt .Sprintf ("expected result not to be an error: %+v" , resp ))
391
+
392
+ textContent , ok = resp .Content [0 ].(mcp.TextContent )
393
+ require .True (t , ok , "expected content to be of type TextContent" )
394
+
395
+ var reviews []struct {
396
+ NodeID string `json:"node_id"`
397
+ }
398
+ err = json .Unmarshal ([]byte (textContent .Text ), & reviews )
399
+ require .NoError (t , err , "expected to unmarshal text content successfully" )
400
+
401
+ // Check our review is the only one in the list
402
+ require .Len (t , reviews , 1 , "expected to find one review" )
403
+ require .Equal (t , pullRequestReviewId , reviews [0 ].NodeID , "expected to find our review in the list" )
404
+ }
0 commit comments