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

Commit5f92434

Browse files
JoannaaKLSamMorrowDrums
authored andcommitted
Add search pull requests tool
1 parentacba284 commit5f92434

File tree

4 files changed

+246
-1
lines changed

4 files changed

+246
-1
lines changed

‎pkg/github/issues.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func AddIssueComment(getClient GetClientFn, t translations.TranslationHelperFunc
153153
}
154154
}
155155

156-
// SearchIssues creates a tool to search for issues and pull requests.
156+
// SearchIssues creates a tool to search for issues.
157157
funcSearchIssues(getClientGetClientFn,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
158158
returnmcp.NewTool("search_issues",
159159
mcp.WithDescription(t("TOOL_SEARCH_ISSUES_DESCRIPTION","Search for issues in GitHub repositories.")),

‎pkg/github/pullrequests.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,94 @@ func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFun
533533
}
534534
}
535535

536+
// SearchPullRequests creates a tool to search for pull requests.
537+
funcSearchPullRequests(getClientGetClientFn,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
538+
returnmcp.NewTool("search_pull_requests",
539+
mcp.WithDescription(t("TOOL_SEARCH_PULL_REQUESTS_DESCRIPTION","Search for pull requests in GitHub repositories.")),
540+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
541+
Title:t("TOOL_SEARCH_PULL_REQUESTS_USER_TITLE","Search pull requests"),
542+
ReadOnlyHint:ToBoolPtr(true),
543+
}),
544+
mcp.WithString("q",
545+
mcp.Required(),
546+
mcp.Description("Search query using GitHub pull request search syntax"),
547+
),
548+
mcp.WithString("sort",
549+
mcp.Description("Sort field by number of matches of categories, defaults to best match"),
550+
mcp.Enum(
551+
"comments",
552+
"reactions",
553+
"reactions-+1",
554+
"reactions--1",
555+
"reactions-smile",
556+
"reactions-thinking_face",
557+
"reactions-heart",
558+
"reactions-tada",
559+
"interactions",
560+
"created",
561+
"updated",
562+
),
563+
),
564+
mcp.WithString("order",
565+
mcp.Description("Sort order"),
566+
mcp.Enum("asc","desc"),
567+
),
568+
WithPagination(),
569+
),
570+
func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
571+
query,err:=RequiredParam[string](request,"q")
572+
iferr!=nil {
573+
returnmcp.NewToolResultError(err.Error()),nil
574+
}
575+
sort,err:=OptionalParam[string](request,"sort")
576+
iferr!=nil {
577+
returnmcp.NewToolResultError(err.Error()),nil
578+
}
579+
order,err:=OptionalParam[string](request,"order")
580+
iferr!=nil {
581+
returnmcp.NewToolResultError(err.Error()),nil
582+
}
583+
pagination,err:=OptionalPaginationParams(request)
584+
iferr!=nil {
585+
returnmcp.NewToolResultError(err.Error()),nil
586+
}
587+
588+
opts:=&github.SearchOptions{
589+
Sort:sort,
590+
Order:order,
591+
ListOptions: github.ListOptions{
592+
PerPage:pagination.perPage,
593+
Page:pagination.page,
594+
},
595+
}
596+
597+
client,err:=getClient(ctx)
598+
iferr!=nil {
599+
returnnil,fmt.Errorf("failed to get GitHub client: %w",err)
600+
}
601+
result,resp,err:=client.Search.Issues(ctx,query,opts)
602+
iferr!=nil {
603+
returnnil,fmt.Errorf("failed to search pull requests: %w",err)
604+
}
605+
deferfunc() {_=resp.Body.Close() }()
606+
607+
ifresp.StatusCode!=http.StatusOK {
608+
body,err:=io.ReadAll(resp.Body)
609+
iferr!=nil {
610+
returnnil,fmt.Errorf("failed to read response body: %w",err)
611+
}
612+
returnmcp.NewToolResultError(fmt.Sprintf("failed to search pull requests: %s",string(body))),nil
613+
}
614+
615+
r,err:=json.Marshal(result)
616+
iferr!=nil {
617+
returnnil,fmt.Errorf("failed to marshal response: %w",err)
618+
}
619+
620+
returnmcp.NewToolResultText(string(r)),nil
621+
}
622+
}
623+
536624
// GetPullRequestFiles creates a tool to get the list of files changed in a pull request.
537625
funcGetPullRequestFiles(getClientGetClientFn,t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
538626
returnmcp.NewTool("get_pull_request_files",

‎pkg/github/pullrequests_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,162 @@ func Test_MergePullRequest(t *testing.T) {
565565
}
566566
}
567567

568+
funcTest_SearchPullRequests(t*testing.T) {
569+
mockClient:=github.NewClient(nil)
570+
tool,_:=SearchPullRequests(stubGetClientFn(mockClient),translations.NullTranslationHelper)
571+
require.NoError(t,toolsnaps.Test(tool.Name,tool))
572+
573+
assert.Equal(t,"search_pull_requests",tool.Name)
574+
assert.NotEmpty(t,tool.Description)
575+
assert.Contains(t,tool.InputSchema.Properties,"q")
576+
assert.Contains(t,tool.InputSchema.Properties,"sort")
577+
assert.Contains(t,tool.InputSchema.Properties,"order")
578+
assert.Contains(t,tool.InputSchema.Properties,"perPage")
579+
assert.Contains(t,tool.InputSchema.Properties,"page")
580+
assert.ElementsMatch(t,tool.InputSchema.Required, []string{"q"})
581+
582+
mockSearchResult:=&github.IssuesSearchResult{
583+
Total:github.Ptr(2),
584+
IncompleteResults:github.Ptr(false),
585+
Issues: []*github.Issue{
586+
{
587+
Number:github.Ptr(42),
588+
Title:github.Ptr("Test PR 1"),
589+
Body:github.Ptr("Updated tests."),
590+
State:github.Ptr("open"),
591+
HTMLURL:github.Ptr("https://github.com/owner/repo/pull/1"),
592+
Comments:github.Ptr(5),
593+
User:&github.User{
594+
Login:github.Ptr("user1"),
595+
},
596+
},
597+
{
598+
Number:github.Ptr(43),
599+
Title:github.Ptr("Test PR 2"),
600+
Body:github.Ptr("Updated build scripts."),
601+
State:github.Ptr("open"),
602+
HTMLURL:github.Ptr("https://github.com/owner/repo/pull/2"),
603+
Comments:github.Ptr(3),
604+
User:&github.User{
605+
Login:github.Ptr("user2"),
606+
},
607+
},
608+
},
609+
}
610+
611+
tests:= []struct {
612+
namestring
613+
mockedClient*http.Client
614+
requestArgsmap[string]interface{}
615+
expectErrorbool
616+
expectedResult*github.IssuesSearchResult
617+
expectedErrMsgstring
618+
}{
619+
{
620+
name:"successful pull request search with all parameters",
621+
mockedClient:mock.NewMockedHTTPClient(
622+
mock.WithRequestMatchHandler(
623+
mock.GetSearchIssues,
624+
expectQueryParams(
625+
t,
626+
map[string]string{
627+
"q":"repo:owner/repo is:pr is:open",
628+
"sort":"created",
629+
"order":"desc",
630+
"page":"1",
631+
"per_page":"30",
632+
},
633+
).andThen(
634+
mockResponse(t,http.StatusOK,mockSearchResult),
635+
),
636+
),
637+
),
638+
requestArgs:map[string]interface{}{
639+
"q":"repo:owner/repo is:pr is:open",
640+
"sort":"created",
641+
"order":"desc",
642+
"page":float64(1),
643+
"perPage":float64(30),
644+
},
645+
expectError:false,
646+
expectedResult:mockSearchResult,
647+
},
648+
{
649+
name:"pull request search with minimal parameters",
650+
mockedClient:mock.NewMockedHTTPClient(
651+
mock.WithRequestMatch(
652+
mock.GetSearchIssues,
653+
mockSearchResult,
654+
),
655+
),
656+
requestArgs:map[string]interface{}{
657+
"q":"repo:owner/repo is:pr is:open",
658+
},
659+
expectError:false,
660+
expectedResult:mockSearchResult,
661+
},
662+
{
663+
name:"search pull requests fails",
664+
mockedClient:mock.NewMockedHTTPClient(
665+
mock.WithRequestMatchHandler(
666+
mock.GetSearchIssues,
667+
http.HandlerFunc(func(w http.ResponseWriter,_*http.Request) {
668+
w.WriteHeader(http.StatusBadRequest)
669+
_,_=w.Write([]byte(`{"message": "Validation Failed"}`))
670+
}),
671+
),
672+
),
673+
requestArgs:map[string]interface{}{
674+
"q":"invalid:query",
675+
},
676+
expectError:true,
677+
expectedErrMsg:"failed to search issues",
678+
},
679+
}
680+
681+
for_,tc:=rangetests {
682+
t.Run(tc.name,func(t*testing.T) {
683+
// Setup client with mock
684+
client:=github.NewClient(tc.mockedClient)
685+
_,handler:=SearchIssues(stubGetClientFn(client),translations.NullTranslationHelper)
686+
687+
// Create call request
688+
request:=createMCPRequest(tc.requestArgs)
689+
690+
// Call handler
691+
result,err:=handler(context.Background(),request)
692+
693+
// Verify results
694+
iftc.expectError {
695+
require.Error(t,err)
696+
assert.Contains(t,err.Error(),tc.expectedErrMsg)
697+
return
698+
}
699+
700+
require.NoError(t,err)
701+
702+
// Parse the result and get the text content if no error
703+
textContent:=getTextResult(t,result)
704+
705+
// Unmarshal and verify the result
706+
varreturnedResult github.IssuesSearchResult
707+
err=json.Unmarshal([]byte(textContent.Text),&returnedResult)
708+
require.NoError(t,err)
709+
assert.Equal(t,*tc.expectedResult.Total,*returnedResult.Total)
710+
assert.Equal(t,*tc.expectedResult.IncompleteResults,*returnedResult.IncompleteResults)
711+
assert.Len(t,returnedResult.Issues,len(tc.expectedResult.Issues))
712+
fori,issue:=rangereturnedResult.Issues {
713+
assert.Equal(t,*tc.expectedResult.Issues[i].Number,*issue.Number)
714+
assert.Equal(t,*tc.expectedResult.Issues[i].Title,*issue.Title)
715+
assert.Equal(t,*tc.expectedResult.Issues[i].State,*issue.State)
716+
assert.Equal(t,*tc.expectedResult.Issues[i].HTMLURL,*issue.HTMLURL)
717+
assert.Equal(t,*tc.expectedResult.Issues[i].User.Login,*issue.User.Login)
718+
}
719+
})
720+
}
721+
722+
}
723+
568724
funcTest_GetPullRequestFiles(t*testing.T) {
569725
// Verify tool definition once
570726
mockClient:=github.NewClient(nil)

‎pkg/github/tools.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func DefaultToolsetGroup(readOnly bool, getClient GetClientFn, getGQLClient GetG
5151
AddReadTools(
5252
toolsets.NewServerTool(GetIssue(getClient,t)),
5353
toolsets.NewServerTool(SearchIssues(getClient,t)),
54+
toolsets.NewServerTool(SearchPullRequests(getClient,t)),
5455
toolsets.NewServerTool(ListIssues(getClient,t)),
5556
toolsets.NewServerTool(GetIssueComments(getClient,t)),
5657
).

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp