Skip to content

Commit b59a27c

Browse files
committed
Merge branch 'main' of github.com:JoannaaKL/github-mcp-server
2 parents 59919f0 + 8bd7152 commit b59a27c

12 files changed

+840
-159
lines changed

pkg/github/__toolsnaps__/get_pull_request_files.snap

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@
1010
"description": "Repository owner",
1111
"type": "string"
1212
},
13+
"page": {
14+
"description": "Page number for pagination (min 1)",
15+
"minimum": 1,
16+
"type": "number"
17+
},
18+
"perPage": {
19+
"description": "Results per page for pagination (min 1, max 100)",
20+
"maximum": 100,
21+
"minimum": 1,
22+
"type": "number"
23+
},
1324
"pullNumber": {
1425
"description": "Pull request number",
1526
"type": "number"

pkg/github/__toolsnaps__/search_issues.snap

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"title": "Search issues",
44
"readOnlyHint": true
55
},
6-
"description": "Search for issues in GitHub repositories.",
6+
"description": "Search for issues in GitHub repositories using issues search syntax already scoped to is:issue",
77
"inputSchema": {
88
"properties": {
99
"order": {
@@ -14,6 +14,10 @@
1414
],
1515
"type": "string"
1616
},
17+
"owner": {
18+
"description": "Optional repository owner. If provided with repo, only notifications for this repository are listed.",
19+
"type": "string"
20+
},
1721
"page": {
1822
"description": "Page number for pagination (min 1)",
1923
"minimum": 1,
@@ -25,10 +29,14 @@
2529
"minimum": 1,
2630
"type": "number"
2731
},
28-
"q": {
32+
"query": {
2933
"description": "Search query using GitHub issues search syntax",
3034
"type": "string"
3135
},
36+
"repo": {
37+
"description": "Optional repository name. If provided with owner, only notifications for this repository are listed.",
38+
"type": "string"
39+
},
3240
"sort": {
3341
"description": "Sort field by number of matches of categories, defaults to best match",
3442
"enum": [
@@ -48,7 +56,7 @@
4856
}
4957
},
5058
"required": [
51-
"q"
59+
"query"
5260
],
5361
"type": "object"
5462
},
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"annotations": {
3+
"title": "Search pull requests",
4+
"readOnlyHint": true
5+
},
6+
"description": "Search for pull requests in GitHub repositories using issues search syntax already scoped to is:pr",
7+
"inputSchema": {
8+
"properties": {
9+
"order": {
10+
"description": "Sort order",
11+
"enum": [
12+
"asc",
13+
"desc"
14+
],
15+
"type": "string"
16+
},
17+
"owner": {
18+
"description": "Optional repository owner. If provided with repo, only notifications for this repository are listed.",
19+
"type": "string"
20+
},
21+
"page": {
22+
"description": "Page number for pagination (min 1)",
23+
"minimum": 1,
24+
"type": "number"
25+
},
26+
"perPage": {
27+
"description": "Results per page for pagination (min 1, max 100)",
28+
"maximum": 100,
29+
"minimum": 1,
30+
"type": "number"
31+
},
32+
"query": {
33+
"description": "Search query using GitHub pull request search syntax",
34+
"type": "string"
35+
},
36+
"repo": {
37+
"description": "Optional repository name. If provided with owner, only notifications for this repository are listed.",
38+
"type": "string"
39+
},
40+
"sort": {
41+
"description": "Sort field by number of matches of categories, defaults to best match",
42+
"enum": [
43+
"comments",
44+
"reactions",
45+
"reactions-+1",
46+
"reactions--1",
47+
"reactions-smile",
48+
"reactions-thinking_face",
49+
"reactions-heart",
50+
"reactions-tada",
51+
"interactions",
52+
"created",
53+
"updated"
54+
],
55+
"type": "string"
56+
}
57+
},
58+
"required": [
59+
"query"
60+
],
61+
"type": "object"
62+
},
63+
"name": "search_pull_requests"
64+
}

pkg/github/__toolsnaps__/search_users.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"title": "Search users",
44
"readOnlyHint": true
55
},
6-
"description": "Search for GitHub users",
6+
"description": "Search for GitHub users exclusively",
77
"inputSchema": {
88
"properties": {
99
"order": {
@@ -25,8 +25,8 @@
2525
"minimum": 1,
2626
"type": "number"
2727
},
28-
"q": {
29-
"description": "Search query using GitHub users search syntax",
28+
"query": {
29+
"description": "Search query using GitHub users search syntax scoped to type:user",
3030
"type": "string"
3131
},
3232
"sort": {
@@ -40,7 +40,7 @@
4040
}
4141
},
4242
"required": [
43-
"q"
43+
"query"
4444
],
4545
"type": "object"
4646
},

pkg/github/issues.go

Lines changed: 10 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -153,18 +153,24 @@ 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
func SearchIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
158158
return mcp.NewTool("search_issues",
159-
mcp.WithDescription(t("TOOL_SEARCH_ISSUES_DESCRIPTION", "Search for issues in GitHub repositories.")),
159+
mcp.WithDescription(t("TOOL_SEARCH_ISSUES_DESCRIPTION", "Search for issues in GitHub repositories using issues search syntax already scoped to is:issue")),
160160
mcp.WithToolAnnotation(mcp.ToolAnnotation{
161161
Title: t("TOOL_SEARCH_ISSUES_USER_TITLE", "Search issues"),
162162
ReadOnlyHint: ToBoolPtr(true),
163163
}),
164-
mcp.WithString("q",
164+
mcp.WithString("query",
165165
mcp.Required(),
166166
mcp.Description("Search query using GitHub issues search syntax"),
167167
),
168+
mcp.WithString("owner",
169+
mcp.Description("Optional repository owner. If provided with repo, only notifications for this repository are listed."),
170+
),
171+
mcp.WithString("repo",
172+
mcp.Description("Optional repository name. If provided with owner, only notifications for this repository are listed."),
173+
),
168174
mcp.WithString("sort",
169175
mcp.Description("Sort field by number of matches of categories, defaults to best match"),
170176
mcp.Enum(
@@ -188,59 +194,7 @@ func SearchIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (
188194
WithPagination(),
189195
),
190196
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
191-
query, err := RequiredParam[string](request, "q")
192-
if err != nil {
193-
return mcp.NewToolResultError(err.Error()), nil
194-
}
195-
if !strings.Contains(query, "is:issue") {
196-
query += " is:issue"
197-
}
198-
sort, err := OptionalParam[string](request, "sort")
199-
if err != nil {
200-
return mcp.NewToolResultError(err.Error()), nil
201-
}
202-
order, err := OptionalParam[string](request, "order")
203-
if err != nil {
204-
return mcp.NewToolResultError(err.Error()), nil
205-
}
206-
pagination, err := OptionalPaginationParams(request)
207-
if err != nil {
208-
return mcp.NewToolResultError(err.Error()), nil
209-
}
210-
211-
opts := &github.SearchOptions{
212-
Sort: sort,
213-
Order: order,
214-
ListOptions: github.ListOptions{
215-
PerPage: pagination.perPage,
216-
Page: pagination.page,
217-
},
218-
}
219-
220-
client, err := getClient(ctx)
221-
if err != nil {
222-
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
223-
}
224-
result, resp, err := client.Search.Issues(ctx, query, opts)
225-
if err != nil {
226-
return nil, fmt.Errorf("failed to search issues: %w", err)
227-
}
228-
defer func() { _ = resp.Body.Close() }()
229-
230-
if resp.StatusCode != http.StatusOK {
231-
body, err := io.ReadAll(resp.Body)
232-
if err != nil {
233-
return nil, fmt.Errorf("failed to read response body: %w", err)
234-
}
235-
return mcp.NewToolResultError(fmt.Sprintf("failed to search issues: %s", string(body))), nil
236-
}
237-
238-
r, err := json.Marshal(result)
239-
if err != nil {
240-
return nil, fmt.Errorf("failed to marshal response: %w", err)
241-
}
242-
243-
return mcp.NewToolResultText(string(r)), nil
197+
return searchHandler(ctx, getClient, request, "issue", "failed to search issues")
244198
}
245199
}
246200

pkg/github/issues_test.go

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,14 @@ func Test_SearchIssues(t *testing.T) {
237237

238238
assert.Equal(t, "search_issues", tool.Name)
239239
assert.NotEmpty(t, tool.Description)
240-
assert.Contains(t, tool.InputSchema.Properties, "q")
240+
assert.Contains(t, tool.InputSchema.Properties, "query")
241+
assert.Contains(t, tool.InputSchema.Properties, "owner")
242+
assert.Contains(t, tool.InputSchema.Properties, "repo")
241243
assert.Contains(t, tool.InputSchema.Properties, "sort")
242244
assert.Contains(t, tool.InputSchema.Properties, "order")
243245
assert.Contains(t, tool.InputSchema.Properties, "perPage")
244246
assert.Contains(t, tool.InputSchema.Properties, "page")
245-
assert.ElementsMatch(t, tool.InputSchema.Required, []string{"q"})
247+
assert.ElementsMatch(t, tool.InputSchema.Required, []string{"query"})
246248

247249
// Setup mock search results
248250
mockSearchResult := &github.IssuesSearchResult{
@@ -290,7 +292,7 @@ func Test_SearchIssues(t *testing.T) {
290292
expectQueryParams(
291293
t,
292294
map[string]string{
293-
"q": "repo:owner/repo is:issue is:open",
295+
"q": "is:issue repo:owner/repo is:open",
294296
"sort": "created",
295297
"order": "desc",
296298
"page": "1",
@@ -302,7 +304,7 @@ func Test_SearchIssues(t *testing.T) {
302304
),
303305
),
304306
requestArgs: map[string]interface{}{
305-
"q": "repo:owner/repo is:issue is:open",
307+
"query": "repo:owner/repo is:open",
306308
"sort": "created",
307309
"order": "desc",
308310
"page": float64(1),
@@ -311,6 +313,83 @@ func Test_SearchIssues(t *testing.T) {
311313
expectError: false,
312314
expectedResult: mockSearchResult,
313315
},
316+
{
317+
name: "issues search with owner and repo parameters",
318+
mockedClient: mock.NewMockedHTTPClient(
319+
mock.WithRequestMatchHandler(
320+
mock.GetSearchIssues,
321+
expectQueryParams(
322+
t,
323+
map[string]string{
324+
"q": "repo:test-owner/test-repo is:issue is:open",
325+
"sort": "created",
326+
"order": "asc",
327+
"page": "1",
328+
"per_page": "30",
329+
},
330+
).andThen(
331+
mockResponse(t, http.StatusOK, mockSearchResult),
332+
),
333+
),
334+
),
335+
requestArgs: map[string]interface{}{
336+
"query": "is:open",
337+
"owner": "test-owner",
338+
"repo": "test-repo",
339+
"sort": "created",
340+
"order": "asc",
341+
},
342+
expectError: false,
343+
expectedResult: mockSearchResult,
344+
},
345+
{
346+
name: "issues search with only owner parameter (should ignore it)",
347+
mockedClient: mock.NewMockedHTTPClient(
348+
mock.WithRequestMatchHandler(
349+
mock.GetSearchIssues,
350+
expectQueryParams(
351+
t,
352+
map[string]string{
353+
"q": "is:issue bug",
354+
"page": "1",
355+
"per_page": "30",
356+
},
357+
).andThen(
358+
mockResponse(t, http.StatusOK, mockSearchResult),
359+
),
360+
),
361+
),
362+
requestArgs: map[string]interface{}{
363+
"query": "bug",
364+
"owner": "test-owner",
365+
},
366+
expectError: false,
367+
expectedResult: mockSearchResult,
368+
},
369+
{
370+
name: "issues search with only repo parameter (should ignore it)",
371+
mockedClient: mock.NewMockedHTTPClient(
372+
mock.WithRequestMatchHandler(
373+
mock.GetSearchIssues,
374+
expectQueryParams(
375+
t,
376+
map[string]string{
377+
"q": "is:issue feature",
378+
"page": "1",
379+
"per_page": "30",
380+
},
381+
).andThen(
382+
mockResponse(t, http.StatusOK, mockSearchResult),
383+
),
384+
),
385+
),
386+
requestArgs: map[string]interface{}{
387+
"query": "feature",
388+
"repo": "test-repo",
389+
},
390+
expectError: false,
391+
expectedResult: mockSearchResult,
392+
},
314393
{
315394
name: "issues search with minimal parameters",
316395
mockedClient: mock.NewMockedHTTPClient(
@@ -320,7 +399,7 @@ func Test_SearchIssues(t *testing.T) {
320399
),
321400
),
322401
requestArgs: map[string]interface{}{
323-
"q": "repo:owner/repo is:issue is:open",
402+
"query": "is:issue repo:owner/repo is:open",
324403
},
325404
expectError: false,
326405
expectedResult: mockSearchResult,
@@ -337,7 +416,7 @@ func Test_SearchIssues(t *testing.T) {
337416
),
338417
),
339418
requestArgs: map[string]interface{}{
340-
"q": "invalid:query",
419+
"query": "invalid:query",
341420
},
342421
expectError: true,
343422
expectedErrMsg: "failed to search issues",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy