Skip to content

Commit fd88ddf

Browse files
authored
Merge branch 'main' into increase_runner_size
2 parents 5e9f1ae + 87d4407 commit fd88ddf

File tree

9 files changed

+168
-188
lines changed

9 files changed

+168
-188
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: "\U0001F41B Bug report"
3-
about: Report a bug or unexpected behavior while using GitHub MCP
3+
about: Report a bug or unexpected behavior while using GitHub MCP Server
44
title: ''
55
labels: bug
66
assignees: ''
@@ -13,7 +13,7 @@ A clear and concise description of what the bug is.
1313

1414
### Affected version
1515

16-
Please run `go run cmd/github-mcp-server/main.go --version` and paste the output below.
16+
Please run ` docker run -i --rm ghcr.io/github/github-mcp-server ./github-mcp-server --version` and paste the output below
1717

1818
### Steps to reproduce the behavior
1919

@@ -27,4 +27,4 @@ A clear and concise description of what you expected to happen and what actually
2727

2828
### Logs
2929

30-
Paste any available logs. Redact if needed.
30+
Paste any available logs. Redact if needed.

.github/pull_request_template.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
Thank you for contributing to GitHub MCP!
2+
Thank you for contributing to GitHub MCP Server!
33
Please reference an existing issue: `Closes #NUMBER`
44
55
Screenshots or videos of changed behavior is incredibly helpful and always appreciated.
@@ -8,4 +8,4 @@
88
- Alternatives: Describe alternative approaches you considered and why you discarded them.
99
-->
1010

11-
Closes:
11+
Closes:

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
130130
- `repo`: Repository name (string, required)
131131
- `title`: Issue title (string, required)
132132
- `body`: Issue body content (string, optional)
133-
- `assignees`: Comma-separated list of usernames to assign to this issue (string, optional)
134-
- `labels`: Comma-separated list of labels to apply to this issue (string, optional)
133+
- `assignees`: Usernames to assign to this issue (string[], optional)
134+
- `labels`: Labels to apply to this issue (string[], optional)
135135

136136
- **add_issue_comment** - Add a comment to an issue
137137

@@ -145,7 +145,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
145145
- `owner`: Repository owner (string, required)
146146
- `repo`: Repository name (string, required)
147147
- `state`: Filter by state ('open', 'closed', 'all') (string, optional)
148-
- `labels`: Comma-separated list of labels to filter by (string, optional)
148+
- `labels`: Labels to filter by (string[], optional)
149149
- `sort`: Sort by ('created', 'updated', 'comments') (string, optional)
150150
- `direction`: Sort direction ('asc', 'desc') (string, optional)
151151
- `since`: Filter by date (ISO 8601 timestamp) (string, optional)
@@ -160,8 +160,8 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
160160
- `title`: New title (string, optional)
161161
- `body`: New description (string, optional)
162162
- `state`: New state ('open' or 'closed') (string, optional)
163-
- `labels`: Comma-separated list of new labels (string, optional)
164-
- `assignees`: Comma-separated list of new assignees (string, optional)
163+
- `labels`: New labels (string[], optional)
164+
- `assignees`: New assignees (string[], optional)
165165
- `milestone`: New milestone number (number, optional)
166166

167167
- **search_issues** - Search for issues and pull requests

pkg/github/helper_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,52 @@ import (
1010
"github.com/stretchr/testify/require"
1111
)
1212

13+
// expectQueryParams is a helper function to create a partial mock that expects a
14+
// request with the given query parameters, with the ability to chain a response handler.
15+
func expectQueryParams(t *testing.T, expectedQueryParams map[string]string) *partialMock {
16+
return &partialMock{
17+
t: t,
18+
expectedQueryParams: expectedQueryParams,
19+
}
20+
}
21+
22+
// expectRequestBody is a helper function to create a partial mock that expects a
23+
// request with the given body, with the ability to chain a response handler.
24+
func expectRequestBody(t *testing.T, expectedRequestBody any) *partialMock {
25+
return &partialMock{
26+
t: t,
27+
expectedRequestBody: expectedRequestBody,
28+
}
29+
}
30+
31+
type partialMock struct {
32+
t *testing.T
33+
expectedQueryParams map[string]string
34+
expectedRequestBody any
35+
}
36+
37+
func (p *partialMock) andThen(responseHandler http.HandlerFunc) http.HandlerFunc {
38+
p.t.Helper()
39+
return func(w http.ResponseWriter, r *http.Request) {
40+
if p.expectedRequestBody != nil {
41+
var unmarshaledRequestBody any
42+
err := json.NewDecoder(r.Body).Decode(&unmarshaledRequestBody)
43+
require.NoError(p.t, err)
44+
45+
require.Equal(p.t, p.expectedRequestBody, unmarshaledRequestBody)
46+
}
47+
48+
if p.expectedQueryParams != nil {
49+
require.Equal(p.t, len(p.expectedQueryParams), len(r.URL.Query()))
50+
for k, v := range p.expectedQueryParams {
51+
require.Equal(p.t, v, r.URL.Query().Get(k))
52+
}
53+
}
54+
55+
responseHandler(w, r)
56+
}
57+
}
58+
1359
// mockResponse is a helper function to create a mock HTTP response handler
1460
// that returns a specified status code and marshaled body.
1561
func mockResponse(t *testing.T, code int, body interface{}) http.HandlerFunc {

pkg/github/issues.go

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,32 @@ func searchIssues(client *github.Client, t translations.TranslationHelperFunc) (
144144
),
145145
mcp.WithString("sort",
146146
mcp.Description("Sort field (comments, reactions, created, etc.)"),
147+
mcp.Enum(
148+
"comments",
149+
"reactions",
150+
"reactions-+1",
151+
"reactions--1",
152+
"reactions-smile",
153+
"reactions-thinking_face",
154+
"reactions-heart",
155+
"reactions-tada",
156+
"interactions",
157+
"created",
158+
"updated",
159+
),
147160
),
148161
mcp.WithString("order",
149162
mcp.Description("Sort order ('asc' or 'desc')"),
163+
mcp.Enum("asc", "desc"),
150164
),
151165
mcp.WithNumber("per_page",
152166
mcp.Description("Results per page (max 100)"),
167+
mcp.Min(1),
168+
mcp.Max(100),
153169
),
154170
mcp.WithNumber("page",
155171
mcp.Description("Page number"),
172+
mcp.Min(1),
156173
),
157174
),
158175
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -228,11 +245,21 @@ func createIssue(client *github.Client, t translations.TranslationHelperFunc) (t
228245
mcp.WithString("body",
229246
mcp.Description("Issue body content"),
230247
),
231-
mcp.WithString("assignees",
232-
mcp.Description("Comma-separate list of usernames to assign to this issue"),
233-
),
234-
mcp.WithString("labels",
235-
mcp.Description("Comma-separate list of labels to apply to this issue"),
248+
mcp.WithArray("assignees",
249+
mcp.Description("Usernames to assign to this issue"),
250+
mcp.Items(
251+
map[string]interface{}{
252+
"type": "string",
253+
},
254+
),
255+
),
256+
mcp.WithArray("labels",
257+
mcp.Description("Labels to apply to this issue"),
258+
mcp.Items(
259+
map[string]interface{}{
260+
"type": "string",
261+
},
262+
),
236263
),
237264
),
238265
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -256,12 +283,13 @@ func createIssue(client *github.Client, t translations.TranslationHelperFunc) (t
256283
}
257284

258285
// Get assignees
259-
assignees, err := optionalCommaSeparatedListParam(request, "assignees")
286+
assignees, err := optionalParam[[]string](request, "assignees")
260287
if err != nil {
261288
return mcp.NewToolResultError(err.Error()), nil
262289
}
290+
263291
// Get labels
264-
labels, err := optionalCommaSeparatedListParam(request, "labels")
292+
labels, err := optionalParam[[]string](request, "labels")
265293
if err != nil {
266294
return mcp.NewToolResultError(err.Error()), nil
267295
}
@@ -311,15 +339,23 @@ func listIssues(client *github.Client, t translations.TranslationHelperFunc) (to
311339
),
312340
mcp.WithString("state",
313341
mcp.Description("Filter by state ('open', 'closed', 'all')"),
342+
mcp.Enum("open", "closed", "all"),
314343
),
315-
mcp.WithString("labels",
316-
mcp.Description("Comma-separated list of labels to filter by"),
344+
mcp.WithArray("labels",
345+
mcp.Description("Filter by labels"),
346+
mcp.Items(
347+
map[string]interface{}{
348+
"type": "string",
349+
},
350+
),
317351
),
318352
mcp.WithString("sort",
319353
mcp.Description("Sort by ('created', 'updated', 'comments')"),
354+
mcp.Enum("created", "updated", "comments"),
320355
),
321356
mcp.WithString("direction",
322357
mcp.Description("Sort direction ('asc', 'desc')"),
358+
mcp.Enum("asc", "desc"),
323359
),
324360
mcp.WithString("since",
325361
mcp.Description("Filter by date (ISO 8601 timestamp)"),
@@ -349,7 +385,8 @@ func listIssues(client *github.Client, t translations.TranslationHelperFunc) (to
349385
return mcp.NewToolResultError(err.Error()), nil
350386
}
351387

352-
opts.Labels, err = optionalCommaSeparatedListParam(request, "labels")
388+
// Get labels
389+
opts.Labels, err = optionalParam[[]string](request, "labels")
353390
if err != nil {
354391
return mcp.NewToolResultError(err.Error()), nil
355392
}
@@ -431,12 +468,23 @@ func updateIssue(client *github.Client, t translations.TranslationHelperFunc) (t
431468
),
432469
mcp.WithString("state",
433470
mcp.Description("New state ('open' or 'closed')"),
434-
),
435-
mcp.WithString("labels",
436-
mcp.Description("Comma-separated list of new labels"),
437-
),
438-
mcp.WithString("assignees",
439-
mcp.Description("Comma-separated list of new assignees"),
471+
mcp.Enum("open", "closed"),
472+
),
473+
mcp.WithArray("labels",
474+
mcp.Description("New labels"),
475+
mcp.Items(
476+
map[string]interface{}{
477+
"type": "string",
478+
},
479+
),
480+
),
481+
mcp.WithArray("assignees",
482+
mcp.Description("New assignees"),
483+
mcp.Items(
484+
map[string]interface{}{
485+
"type": "string",
486+
},
487+
),
440488
),
441489
mcp.WithNumber("milestone",
442490
mcp.Description("New milestone number"),
@@ -484,15 +532,17 @@ func updateIssue(client *github.Client, t translations.TranslationHelperFunc) (t
484532
issueRequest.State = github.Ptr(state)
485533
}
486534

487-
labels, err := optionalCommaSeparatedListParam(request, "labels")
535+
// Get labels
536+
labels, err := optionalParam[[]string](request, "labels")
488537
if err != nil {
489538
return mcp.NewToolResultError(err.Error()), nil
490539
}
491540
if len(labels) > 0 {
492541
issueRequest.Labels = &labels
493542
}
494543

495-
assignees, err := optionalCommaSeparatedListParam(request, "assignees")
544+
// Get assignees
545+
assignees, err := optionalParam[[]string](request, "assignees")
496546
if err != nil {
497547
return mcp.NewToolResultError(err.Error()), nil
498548
}

pkg/github/issues_test.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -418,16 +418,23 @@ func Test_CreateIssue(t *testing.T) {
418418
mockedClient: mock.NewMockedHTTPClient(
419419
mock.WithRequestMatchHandler(
420420
mock.PostReposIssuesByOwnerByRepo,
421-
mockResponse(t, http.StatusCreated, mockIssue),
421+
expectRequestBody(t, map[string]any{
422+
"title": "Test Issue",
423+
"body": "This is a test issue",
424+
"labels": []any{"bug", "help wanted"},
425+
"assignees": []any{"user1", "user2"},
426+
}).andThen(
427+
mockResponse(t, http.StatusCreated, mockIssue),
428+
),
422429
),
423430
),
424431
requestArgs: map[string]interface{}{
425432
"owner": "owner",
426433
"repo": "repo",
427434
"title": "Test Issue",
428435
"body": "This is a test issue",
429-
"assignees": "user1, user2",
430-
"labels": "bug, help wanted",
436+
"assignees": []string{"user1", "user2"},
437+
"labels": []string{"bug", "help wanted"},
431438
},
432439
expectError: false,
433440
expectedIssue: mockIssue,
@@ -606,16 +613,26 @@ func Test_ListIssues(t *testing.T) {
606613
{
607614
name: "list issues with all parameters",
608615
mockedClient: mock.NewMockedHTTPClient(
609-
mock.WithRequestMatch(
616+
mock.WithRequestMatchHandler(
610617
mock.GetReposIssuesByOwnerByRepo,
611-
mockIssues,
618+
expectQueryParams(t, map[string]string{
619+
"state": "open",
620+
"labels": "bug,enhancement",
621+
"sort": "created",
622+
"direction": "desc",
623+
"since": "2023-01-01T00:00:00Z",
624+
"page": "1",
625+
"per_page": "30",
626+
}).andThen(
627+
mockResponse(t, http.StatusOK, mockIssues),
628+
),
612629
),
613630
),
614631
requestArgs: map[string]interface{}{
615632
"owner": "owner",
616633
"repo": "repo",
617634
"state": "open",
618-
"labels": "bug,enhancement",
635+
"labels": []string{"bug", "enhancement"},
619636
"sort": "created",
620637
"direction": "desc",
621638
"since": "2023-01-01T00:00:00Z",
@@ -750,7 +767,16 @@ func Test_UpdateIssue(t *testing.T) {
750767
mockedClient: mock.NewMockedHTTPClient(
751768
mock.WithRequestMatchHandler(
752769
mock.PatchReposIssuesByOwnerByRepoByIssueNumber,
753-
mockResponse(t, http.StatusOK, mockIssue),
770+
expectRequestBody(t, map[string]any{
771+
"title": "Updated Issue Title",
772+
"body": "Updated issue description",
773+
"state": "closed",
774+
"labels": []any{"bug", "priority"},
775+
"assignees": []any{"assignee1", "assignee2"},
776+
"milestone": float64(5),
777+
}).andThen(
778+
mockResponse(t, http.StatusOK, mockIssue),
779+
),
754780
),
755781
),
756782
requestArgs: map[string]interface{}{
@@ -760,8 +786,8 @@ func Test_UpdateIssue(t *testing.T) {
760786
"title": "Updated Issue Title",
761787
"body": "Updated issue description",
762788
"state": "closed",
763-
"labels": "bug,priority",
764-
"assignees": "assignee1,assignee2",
789+
"labels": []string{"bug", "priority"},
790+
"assignees": []string{"assignee1", "assignee2"},
765791
"milestone": float64(5),
766792
},
767793
expectError: false,

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