Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit762e9e1

Browse files
committed
WIP: copilot as reviewer
1 parent70d47de commit762e9e1

File tree

3 files changed

+197
-10
lines changed

3 files changed

+197
-10
lines changed

‎e2e/e2e_test.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,3 +913,148 @@ func TestPullRequestReviewDeletion(t *testing.T) {
913913
require.Len(t,noReviews,0,"expected to find no reviews")
914914

915915
}
916+
917+
funcTestRequestCopilotReview(t*testing.T) {
918+
t.Parallel()
919+
920+
mcpClient:=setupMCPClient(t)
921+
922+
ctx:=context.Background()
923+
924+
// First, who am I
925+
getMeRequest:= mcp.CallToolRequest{}
926+
getMeRequest.Params.Name="get_me"
927+
928+
t.Log("Getting current user...")
929+
resp,err:=mcpClient.CallTool(ctx,getMeRequest)
930+
require.NoError(t,err,"expected to call 'get_me' tool successfully")
931+
require.False(t,resp.IsError,fmt.Sprintf("expected result not to be an error: %+v",resp))
932+
933+
require.False(t,resp.IsError,"expected result not to be an error")
934+
require.Len(t,resp.Content,1,"expected content to have one item")
935+
936+
textContent,ok:=resp.Content[0].(mcp.TextContent)
937+
require.True(t,ok,"expected content to be of type TextContent")
938+
939+
vartrimmedGetMeTextstruct {
940+
Loginstring`json:"login"`
941+
}
942+
err=json.Unmarshal([]byte(textContent.Text),&trimmedGetMeText)
943+
require.NoError(t,err,"expected to unmarshal text content successfully")
944+
945+
currentOwner:=trimmedGetMeText.Login
946+
947+
// Then create a repository with a README (via autoInit)
948+
repoName:=fmt.Sprintf("github-mcp-server-e2e-%s-%d",t.Name(),time.Now().UnixMilli())
949+
createRepoRequest:= mcp.CallToolRequest{}
950+
createRepoRequest.Params.Name="create_repository"
951+
createRepoRequest.Params.Arguments=map[string]any{
952+
"name":repoName,
953+
"private":true,
954+
"autoInit":true,
955+
}
956+
957+
t.Logf("Creating repository %s/%s...",currentOwner,repoName)
958+
_,err=mcpClient.CallTool(ctx,createRepoRequest)
959+
require.NoError(t,err,"expected to call 'get_me' tool successfully")
960+
require.False(t,resp.IsError,fmt.Sprintf("expected result not to be an error: %+v",resp))
961+
962+
// Cleanup the repository after the test
963+
t.Cleanup(func() {
964+
// MCP Server doesn't support deletions, but we can use the GitHub Client
965+
ghClient:=gogithub.NewClient(nil).WithAuthToken(getE2EToken(t))
966+
t.Logf("Deleting repository %s/%s...",currentOwner,repoName)
967+
_,err:=ghClient.Repositories.Delete(context.Background(),currentOwner,repoName)
968+
require.NoError(t,err,"expected to delete repository successfully")
969+
})
970+
971+
// Create a branch on which to create a new commit
972+
createBranchRequest:= mcp.CallToolRequest{}
973+
createBranchRequest.Params.Name="create_branch"
974+
createBranchRequest.Params.Arguments=map[string]any{
975+
"owner":currentOwner,
976+
"repo":repoName,
977+
"branch":"test-branch",
978+
"from_branch":"main",
979+
}
980+
981+
t.Logf("Creating branch in %s/%s...",currentOwner,repoName)
982+
resp,err=mcpClient.CallTool(ctx,createBranchRequest)
983+
require.NoError(t,err,"expected to call 'create_branch' tool successfully")
984+
require.False(t,resp.IsError,fmt.Sprintf("expected result not to be an error: %+v",resp))
985+
986+
// Create a commit with a new file
987+
commitRequest:= mcp.CallToolRequest{}
988+
commitRequest.Params.Name="create_or_update_file"
989+
commitRequest.Params.Arguments=map[string]any{
990+
"owner":currentOwner,
991+
"repo":repoName,
992+
"path":"test-file.txt",
993+
"content":fmt.Sprintf("Created by e2e test %s",t.Name()),
994+
"message":"Add test file",
995+
"branch":"test-branch",
996+
}
997+
998+
t.Logf("Creating commit with new file in %s/%s...",currentOwner,repoName)
999+
resp,err=mcpClient.CallTool(ctx,commitRequest)
1000+
require.NoError(t,err,"expected to call 'create_or_update_file' tool successfully")
1001+
require.False(t,resp.IsError,fmt.Sprintf("expected result not to be an error: %+v",resp))
1002+
1003+
textContent,ok=resp.Content[0].(mcp.TextContent)
1004+
require.True(t,ok,"expected content to be of type TextContent")
1005+
1006+
vartrimmedCommitTextstruct {
1007+
SHAstring`json:"sha"`
1008+
}
1009+
err=json.Unmarshal([]byte(textContent.Text),&trimmedCommitText)
1010+
require.NoError(t,err,"expected to unmarshal text content successfully")
1011+
commitId:=trimmedCommitText.SHA
1012+
1013+
// Create a pull request
1014+
prRequest:= mcp.CallToolRequest{}
1015+
prRequest.Params.Name="create_pull_request"
1016+
prRequest.Params.Arguments=map[string]any{
1017+
"owner":currentOwner,
1018+
"repo":repoName,
1019+
"title":"Test PR",
1020+
"body":"This is a test PR",
1021+
"head":"test-branch",
1022+
"base":"main",
1023+
"commitId":commitId,
1024+
}
1025+
1026+
t.Logf("Creating pull request in %s/%s...",currentOwner,repoName)
1027+
resp,err=mcpClient.CallTool(ctx,prRequest)
1028+
require.NoError(t,err,"expected to call 'create_pull_request' tool successfully")
1029+
require.False(t,resp.IsError,fmt.Sprintf("expected result not to be an error: %+v",resp))
1030+
1031+
// Request a copilot review
1032+
requestCopilotReviewRequest:= mcp.CallToolRequest{}
1033+
requestCopilotReviewRequest.Params.Name="request_copilot_review"
1034+
requestCopilotReviewRequest.Params.Arguments=map[string]any{
1035+
"owner":currentOwner,
1036+
"repo":repoName,
1037+
"pullNumber":1,
1038+
}
1039+
1040+
t.Logf("Requesting Copilot review for pull request in %s/%s...",currentOwner,repoName)
1041+
resp,err=mcpClient.CallTool(ctx,requestCopilotReviewRequest)
1042+
require.NoError(t,err,"expected to call 'request_copilot_review' tool successfully")
1043+
require.False(t,resp.IsError,fmt.Sprintf("expected result not to be an error: %+v",resp))
1044+
1045+
textContent,ok=resp.Content[0].(mcp.TextContent)
1046+
require.True(t,ok,"expected content to be of type TextContent")
1047+
require.Equal(t,"",textContent.Text,"expected content to be empty")
1048+
1049+
// Finally, get requested reviews and see copilot is in there
1050+
// MCP Server doesn't support requesting reviews yet, but we can use the GitHub Client
1051+
ghClient:=gogithub.NewClient(nil).WithAuthToken(getE2EToken(t))
1052+
t.Logf("Getting reviews for pull request in %s/%s...",currentOwner,repoName)
1053+
reviewRequests,_,err:=ghClient.PullRequests.ListReviewers(context.Background(),currentOwner,repoName,1,nil)
1054+
require.NoError(t,err,"expected to get review requests successfully")
1055+
1056+
// Check that there is one review request from copilot
1057+
require.Len(t,reviewRequests.Users,1,"expected to find one review request")
1058+
require.Equal(t,"Copilot",*reviewRequests.Users[0].Login,"expected review request to be for Copilot")
1059+
require.Equal(t,"Bot",*reviewRequests.Users[0].Type,"expected review request to be for Bot")
1060+
}

‎pkg/github/pullrequests.go

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,8 +1730,40 @@ func newGQLStringlike[T ~string](s string) *T {
17301730
return&stringlike
17311731
}
17321732

1733+
typerequestCopilotReviewArgsstruct {
1734+
Ownerstring
1735+
Repostring
1736+
PullNumberint32
1737+
}
1738+
1739+
// TODO: This, and all the param parsing absolutely does not need the MCP request, it just needs the
1740+
// Argument map. Ideally we would just get the byte array and unmarshal it into the struct but mcp-go
1741+
// doesn't expose that.
1742+
funcparseRequestCopilotReviewArgs(request mcp.CallToolRequest) (requestCopilotReviewArgs,error) {
1743+
owner,err:=requiredParam[string](request,"owner")
1744+
iferr!=nil {
1745+
returnrequestCopilotReviewArgs{},err
1746+
}
1747+
1748+
repo,err:=requiredParam[string](request,"repo")
1749+
iferr!=nil {
1750+
returnrequestCopilotReviewArgs{},err
1751+
}
1752+
1753+
pullNumber,err:=requiredParam[constrainableInt32](request,"pullNumber")
1754+
iferr!=nil {
1755+
returnrequestCopilotReviewArgs{},err
1756+
}
1757+
1758+
returnrequestCopilotReviewArgs{
1759+
Owner:owner,
1760+
Repo:repo,
1761+
PullNumber:int32(pullNumber),
1762+
},nil
1763+
}
1764+
17331765
// RequestCopilotReview creates a tool to request a Copilot review for a pull request.
1734-
funcRequestCopilotReview(getClientGetClientFn,t translations.TranslationHelperFunc) (toolmcp.Tool,handler server.ToolHandlerFunc) {
1766+
funcRequestCopilotReview(getClientGetClientFn,t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
17351767
returnmcp.NewTool("request_copilot_review",
17361768
mcp.WithDescription(t("TOOL_REQUEST_COPILOT_REVIEW_DESCRIPTION","Request a GitHub Copilot review for a pull request. Note: This feature depends on GitHub API support and may not be available for all users.")),
17371769
mcp.WithString("owner",
@@ -1742,27 +1774,35 @@ func RequestCopilotReview(getClient GetClientFn, t translations.TranslationHelpe
17421774
mcp.Required(),
17431775
mcp.Description("Repository name"),
17441776
),
1745-
mcp.WithNumber("pull_number",
1777+
mcp.WithNumber("pullNumber",
17461778
mcp.Required(),
17471779
mcp.Description("Pull request number"),
17481780
),
17491781
),
17501782
func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
1751-
owner,err:=requiredParam[string](request,"owner")
1783+
args,err:=parseRequestCopilotReviewArgs(request)
17521784
iferr!=nil {
1753-
returnmcp.NewToolResultError(err.Error()),nil
1785+
returnnil,err
17541786
}
1755-
repo,err:=requiredParam[string](request,"repo")
1787+
1788+
client,err:=getClient(ctx)
17561789
iferr!=nil {
17571790
returnmcp.NewToolResultError(err.Error()),nil
17581791
}
1759-
pullNumber,err:=RequiredInt(request,"pull_number")
1760-
iferr!=nil {
1792+
1793+
if_,_,err:=client.PullRequests.RequestReviewers(
1794+
ctx,
1795+
args.Owner,
1796+
args.Repo,
1797+
int(args.PullNumber),
1798+
github.ReviewersRequest{
1799+
Reviewers: []string{"copilot-pull-request-reviewer[bot]"},// The login name of the copilot bot.
1800+
},
1801+
);err!=nil {
17611802
returnmcp.NewToolResultError(err.Error()),nil
17621803
}
17631804

1764-
// As of now, GitHub API does not support Copilot as a reviewer programmatically.
1765-
// This is a placeholder for future support.
1766-
returnmcp.NewToolResultError(fmt.Sprintf("Requesting a Copilot review for PR #%d in %s/%s is not currently supported by the GitHub API. Please request a Copilot review via the GitHub UI.",pullNumber,owner,repo)),nil
1805+
// Return nothing, just indicate success for the time being.
1806+
returnmcp.NewToolResultText(""),nil
17671807
}
17681808
}

‎pkg/github/tools.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ func InitToolsets(passedToolsets []string, readOnly bool, getClient GetClientFn,
7777
toolsets.NewServerTool(AddPullRequestReviewCommentToPendingReview(getGQLClient,t)),
7878
toolsets.NewServerTool(SubmitPendingPullRequestReview(getGQLClient,t)),
7979
toolsets.NewServerTool(DeletePendingPullRequestReview(getGQLClient,t)),
80+
81+
toolsets.NewServerTool(RequestCopilotReview(getClient,t)),
8082
)
8183
codeSecurity:=toolsets.NewToolset("code_security","Code security related tools, such as GitHub Code Scanning").
8284
AddReadTools(

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp