Skip to content

Commit 0474365

Browse files
authored
Merge branch 'main' into feat/259/assign-reviewers
2 parents 432907e + 3341e6b commit 0474365

14 files changed

+140
-105
lines changed

CONTRIBUTING.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@ Please note that this project is released with a [Contributor Code of Conduct](C
1414

1515
These are one time installations required to be able to test your changes locally as part of the pull request (PR) submission process.
1616

17-
1. install Go [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go)
18-
1. [install golangci-lint v2](https://golangci-lint.run/welcome/install/#local-installation)
17+
1. Install Go [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go)
18+
2. [Install golangci-lint v2](https://golangci-lint.run/welcome/install/#local-installation)
1919

2020
## Submitting a pull request
2121

22-
> **Important**: Please open your pull request against the `next` branch, not `main`. The `next` branch is where we integrate new features and changes before they are merged to `main`.
23-
2422
1. [Fork][fork] and clone the repository
25-
1. Make sure the tests pass on your machine: `go test -v ./...`
26-
1. Make sure linter passes on your machine: `golangci-lint run`
27-
1. Create a new branch: `git checkout -b my-branch-name`
28-
1. Make your change, add tests, and make sure the tests and linter still pass
29-
1. Push to your fork and [submit a pull request][pr] targeting the `next` branch
30-
1. Pat yourself on the back and wait for your pull request to be reviewed and merged.
23+
2. Make sure the tests pass on your machine: `go test -v ./...`
24+
3. Make sure linter passes on your machine: `golangci-lint run`
25+
4. Create a new branch: `git checkout -b my-branch-name`
26+
5. Add your changes and tests, and make sure the Action workflows still pass
27+
- Run linter: `script/lint`
28+
- Update snapshots and run tests: `UPDATE_TOOLSNAPS=true go test ./...`
29+
- Update readme documentation: `script/generate-docs`
30+
6. Push to your fork and [submit a pull request][pr] targeting the `main` branch
31+
7. Pat yourself on the back and wait for your pull request to be reviewed and merged.
3132

3233
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
3334

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
550550
<summary>Context</summary>
551551

552552
- **get_me** - Get my user profile
553-
- `reason`: Optional: the reason for requesting the user information (string, optional)
553+
- No parameters required
554554

555555
</details>
556556

@@ -871,7 +871,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
871871
- `owner`: Repository owner (username or organization) (string, required)
872872
- `path`: Path where to create/update the file (string, required)
873873
- `repo`: Repository name (string, required)
874-
- `sha`: SHA of file being replaced (for updates) (string, optional)
874+
- `sha`: Required if updating an existing file. The blob SHA of the file being replaced. (string, optional)
875875

876876
- **create_repository** - Create repository
877877
- `autoInit`: Initialize with README (boolean, optional)
@@ -903,7 +903,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
903903
- `path`: Path to file/directory (directories must end with a slash '/') (string, required)
904904
- `ref`: Accepts optional git refs such as `refs/tags/{tag}`, `refs/heads/{branch}` or `refs/pull/{pr_number}/head` (string, optional)
905905
- `repo`: Repository name (string, required)
906-
- `sha`: Accepts optional git sha, if sha is specified it will be used instead of ref (string, optional)
906+
- `sha`: Accepts optional commit SHA. If specified, it will be used instead of ref (string, optional)
907907

908908
- **get_tag** - Get tag details
909909
- `owner`: Repository owner (string, required)
@@ -917,12 +917,12 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
917917
- `repo`: Repository name (string, required)
918918

919919
- **list_commits** - List commits
920-
- `author`: Author username or email address (string, optional)
920+
- `author`: Author username or email address to filter commits by (string, optional)
921921
- `owner`: Repository owner (string, required)
922922
- `page`: Page number for pagination (min 1) (number, optional)
923923
- `perPage`: Results per page for pagination (min 1, max 100) (number, optional)
924924
- `repo`: Repository name (string, required)
925-
- `sha`: The commit SHA, branch name, or tag name to list commits from. If not specified, defaults to the repository's default branch. (string, optional)
925+
- `sha`: Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch of the repository. If a commit SHA is provided, will list commits up to that SHA. (string, optional)
926926

927927
- **list_tags** - List tags
928928
- `owner`: Repository owner (string, required)

docs/host-integration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ flowchart LR
6464
- **Local MCP Server**: An MCP Server running locally, side-by-side with the Application.
6565
- **Remote MCP Server**: An MCP Server running remotely, accessed via the internet. Most Remote MCP Servers require authentication via OAuth.
6666

67-
For more detail, see the [official MCP specification](https://modelcontextprotocol.io/specification/draft).
67+
For more detail, see the [official MCP specification](https://modelcontextprotocol.io/specification/2025-06-18).
6868

6969
> [!NOTE]
7070
> GitHub offers both a Local MCP Server and a Remote MCP Server.
@@ -84,7 +84,7 @@ For the Remote GitHub MCP Server, the recommended way to obtain a valid access t
8484
> The Remote GitHub MCP Server itself does not provide Authentication services.
8585
> Your client application must obtain valid GitHub access tokens through one of the supported methods.
8686
87-
The expected flow for obtaining a valid access token via OAuth is depicted in the [MCP Specification](https://modelcontextprotocol.io/specification/draft/basic/authorization#authorization-flow-steps). For convenience, we've embedded a copy of the authorization flow below. Please study it carefully as the remainder of this document is written with this flow in mind.
87+
The expected flow for obtaining a valid access token via OAuth is depicted in the [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization#authorization-flow-steps). For convenience, we've embedded a copy of the authorization flow below. Please study it carefully as the remainder of this document is written with this flow in mind.
8888

8989
```mermaid
9090
sequenceDiagram

pkg/github/__toolsnaps__/create_or_update_file.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"type": "string"
3232
},
3333
"sha": {
34-
"description": "SHA of file being replaced (for updates)",
34+
"description": "Required if updating an existing file. The blob SHA of the file being replaced.",
3535
"type": "string"
3636
}
3737
},

pkg/github/__toolsnaps__/get_file_contents.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"type": "string"
2424
},
2525
"sha": {
26-
"description": "Accepts optional git sha, if sha is specified it will be used instead of ref",
26+
"description": "Accepts optional commit SHA. If specified, it will be used instead of ref",
2727
"type": "string"
2828
}
2929
},

pkg/github/__toolsnaps__/get_me.snap

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,9 @@
33
"title": "Get my user profile",
44
"readOnlyHint": true
55
},
6-
"description": "Get details of the authenticated GitHub user. Use this when a request includes \"me\", \"my\". The output will not change unless the user changes their profile, so only call this once.",
6+
"description": "Get details of the authenticated GitHub user. Use this when a request is about the user's own profile for GitHub. Or when information is missing to build other tool calls.",
77
"inputSchema": {
8-
"properties": {
9-
"reason": {
10-
"description": "Optional: the reason for requesting the user information",
11-
"type": "string"
12-
}
13-
},
8+
"properties": {},
149
"type": "object"
1510
},
1611
"name": "get_me"

pkg/github/__toolsnaps__/list_commits.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"inputSchema": {
88
"properties": {
99
"author": {
10-
"description": "Author username or email address",
10+
"description": "Author username or email address to filter commits by",
1111
"type": "string"
1212
},
1313
"owner": {
@@ -30,7 +30,7 @@
3030
"type": "string"
3131
},
3232
"sha": {
33-
"description": "The commit SHA, branch name, or tag name to list commits from. If not specified, defaults to the repository's default branch.",
33+
"description": "Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch of the repository. If a commit SHA is provided, will list commits up to that SHA.",
3434
"type": "string"
3535
}
3636
},

pkg/github/context_tools.go

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,44 @@ package github
22

33
import (
44
"context"
5+
"time"
56

67
ghErrors "github.com/github/github-mcp-server/pkg/errors"
78
"github.com/github/github-mcp-server/pkg/translations"
89
"github.com/mark3labs/mcp-go/mcp"
910
"github.com/mark3labs/mcp-go/server"
1011
)
1112

13+
// UserDetails contains additional fields about a GitHub user not already
14+
// present in MinimalUser. Used by get_me context tool but omitted from search_users.
15+
type UserDetails struct {
16+
Name string `json:"name,omitempty"`
17+
Company string `json:"company,omitempty"`
18+
Blog string `json:"blog,omitempty"`
19+
Location string `json:"location,omitempty"`
20+
Email string `json:"email,omitempty"`
21+
Hireable bool `json:"hireable,omitempty"`
22+
Bio string `json:"bio,omitempty"`
23+
TwitterUsername string `json:"twitter_username,omitempty"`
24+
PublicRepos int `json:"public_repos"`
25+
PublicGists int `json:"public_gists"`
26+
Followers int `json:"followers"`
27+
Following int `json:"following"`
28+
CreatedAt time.Time `json:"created_at"`
29+
UpdatedAt time.Time `json:"updated_at"`
30+
PrivateGists int `json:"private_gists,omitempty"`
31+
TotalPrivateRepos int64 `json:"total_private_repos,omitempty"`
32+
OwnedPrivateRepos int64 `json:"owned_private_repos,omitempty"`
33+
}
34+
1235
// GetMe creates a tool to get details of the authenticated user.
1336
func GetMe(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
1437
tool := mcp.NewTool("get_me",
15-
mcp.WithDescription(t("TOOL_GET_ME_DESCRIPTION", "Get details of the authenticated GitHub user. Use this when a request includes \"me\", \"my\". The output will not change unless the user changes their profile, so only call this once.")),
38+
mcp.WithDescription(t("TOOL_GET_ME_DESCRIPTION", "Get details of the authenticated GitHub user. Use this when a request is about the user's own profile for GitHub. Or when information is missing to build other tool calls.")),
1639
mcp.WithToolAnnotation(mcp.ToolAnnotation{
1740
Title: t("TOOL_GET_ME_USER_TITLE", "Get my user profile"),
1841
ReadOnlyHint: ToBoolPtr(true),
1942
}),
20-
mcp.WithString("reason",
21-
mcp.Description("Optional: the reason for requesting the user information"),
22-
),
2343
)
2444

2545
type args struct{}
@@ -38,7 +58,34 @@ func GetMe(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Too
3858
), nil
3959
}
4060

41-
return MarshalledTextResult(user), nil
61+
// Create minimal user representation instead of returning full user object
62+
minimalUser := MinimalUser{
63+
Login: user.GetLogin(),
64+
ID: user.GetID(),
65+
ProfileURL: user.GetHTMLURL(),
66+
AvatarURL: user.GetAvatarURL(),
67+
Details: &UserDetails{
68+
Name: user.GetName(),
69+
Company: user.GetCompany(),
70+
Blog: user.GetBlog(),
71+
Location: user.GetLocation(),
72+
Email: user.GetEmail(),
73+
Hireable: user.GetHireable(),
74+
Bio: user.GetBio(),
75+
TwitterUsername: user.GetTwitterUsername(),
76+
PublicRepos: user.GetPublicRepos(),
77+
PublicGists: user.GetPublicGists(),
78+
Followers: user.GetFollowers(),
79+
Following: user.GetFollowing(),
80+
CreatedAt: user.GetCreatedAt().Time,
81+
UpdatedAt: user.GetUpdatedAt().Time,
82+
PrivateGists: user.GetPrivateGists(),
83+
TotalPrivateRepos: user.GetTotalPrivateRepos(),
84+
OwnedPrivateRepos: user.GetOwnedPrivateRepos(),
85+
},
86+
}
87+
88+
return MarshalledTextResult(minimalUser), nil
4289
})
4390

4491
return tool, handler

pkg/github/context_tools_test.go

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,17 @@ func Test_GetMe(t *testing.T) {
2626

2727
// Setup mock user response
2828
mockUser := &github.User{
29-
Login: github.Ptr("testuser"),
30-
Name: github.Ptr("Test User"),
31-
Email: github.Ptr("test@example.com"),
32-
Bio: github.Ptr("GitHub user for testing"),
33-
Company: github.Ptr("Test Company"),
34-
Location: github.Ptr("Test Location"),
35-
HTMLURL: github.Ptr("https://github.com/testuser"),
36-
CreatedAt: &github.Timestamp{Time: time.Now().Add(-365 * 24 * time.Hour)},
37-
Type: github.Ptr("User"),
29+
Login: github.Ptr("testuser"),
30+
Name: github.Ptr("Test User"),
31+
Email: github.Ptr("test@example.com"),
32+
Bio: github.Ptr("GitHub user for testing"),
33+
Company: github.Ptr("Test Company"),
34+
Location: github.Ptr("Test Location"),
35+
HTMLURL: github.Ptr("https://github.com/testuser"),
36+
CreatedAt: &github.Timestamp{Time: time.Now().Add(-365 * 24 * time.Hour)},
37+
Type: github.Ptr("User"),
38+
Hireable: github.Ptr(true),
39+
TwitterUsername: github.Ptr("testuser_twitter"),
3840
Plan: &github.Plan{
3941
Name: github.Ptr("pro"),
4042
},
@@ -117,17 +119,23 @@ func Test_GetMe(t *testing.T) {
117119
}
118120

119121
// Unmarshal and verify the result
120-
var returnedUser github.User
122+
var returnedUser MinimalUser
121123
err = json.Unmarshal([]byte(textContent.Text), &returnedUser)
122124
require.NoError(t, err)
123125

126+
// Verify minimal user details
127+
assert.Equal(t, *tc.expectedUser.Login, returnedUser.Login)
128+
assert.Equal(t, *tc.expectedUser.HTMLURL, returnedUser.ProfileURL)
129+
124130
// Verify user details
125-
assert.Equal(t, *tc.expectedUser.Login, *returnedUser.Login)
126-
assert.Equal(t, *tc.expectedUser.Name, *returnedUser.Name)
127-
assert.Equal(t, *tc.expectedUser.Email, *returnedUser.Email)
128-
assert.Equal(t, *tc.expectedUser.Bio, *returnedUser.Bio)
129-
assert.Equal(t, *tc.expectedUser.HTMLURL, *returnedUser.HTMLURL)
130-
assert.Equal(t, *tc.expectedUser.Type, *returnedUser.Type)
131+
require.NotNil(t, returnedUser.Details)
132+
assert.Equal(t, *tc.expectedUser.Name, returnedUser.Details.Name)
133+
assert.Equal(t, *tc.expectedUser.Email, returnedUser.Details.Email)
134+
assert.Equal(t, *tc.expectedUser.Bio, returnedUser.Details.Bio)
135+
assert.Equal(t, *tc.expectedUser.Company, returnedUser.Details.Company)
136+
assert.Equal(t, *tc.expectedUser.Location, returnedUser.Details.Location)
137+
assert.Equal(t, *tc.expectedUser.Hireable, returnedUser.Details.Hireable)
138+
assert.Equal(t, *tc.expectedUser.TwitterUsername, returnedUser.Details.TwitterUsername)
131139
})
132140
}
133141
}

pkg/github/discussions.go

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func ListDiscussions(getGQLClient GetGQLClientFn, t translations.TranslationHelp
6262
}
6363

6464
// Now execute the discussions query
65-
var discussions []*github.Issue
65+
var discussions []*github.Discussion
6666
if categoryID != nil {
6767
// Query with category filter (server-side filtering)
6868
var query struct {
@@ -89,17 +89,15 @@ func ListDiscussions(getGQLClient GetGQLClientFn, t translations.TranslationHelp
8989
return mcp.NewToolResultError(err.Error()), nil
9090
}
9191

92-
// Map nodes to GitHub Issue objects
92+
// Map nodes to GitHub Discussion objects
9393
for _, n := range query.Repository.Discussions.Nodes {
94-
di := &github.Issue{
94+
di := &github.Discussion{
9595
Number: github.Ptr(int(n.Number)),
9696
Title: github.Ptr(string(n.Title)),
9797
HTMLURL: github.Ptr(string(n.URL)),
9898
CreatedAt: &github.Timestamp{Time: n.CreatedAt.Time},
99-
Labels: []*github.Label{
100-
{
101-
Name: github.Ptr(fmt.Sprintf("category:%s", string(n.Category.Name))),
102-
},
99+
DiscussionCategory: &github.DiscussionCategory{
100+
Name: github.Ptr(string(n.Category.Name)),
103101
},
104102
}
105103
discussions = append(discussions, di)
@@ -129,17 +127,15 @@ func ListDiscussions(getGQLClient GetGQLClientFn, t translations.TranslationHelp
129127
return mcp.NewToolResultError(err.Error()), nil
130128
}
131129

132-
// Map nodes to GitHub Issue objects
130+
// Map nodes to GitHub Discussion objects
133131
for _, n := range query.Repository.Discussions.Nodes {
134-
di := &github.Issue{
132+
di := &github.Discussion{
135133
Number: github.Ptr(int(n.Number)),
136134
Title: github.Ptr(string(n.Title)),
137135
HTMLURL: github.Ptr(string(n.URL)),
138136
CreatedAt: &github.Timestamp{Time: n.CreatedAt.Time},
139-
Labels: []*github.Label{
140-
{
141-
Name: github.Ptr(fmt.Sprintf("category:%s", string(n.Category.Name))),
142-
},
137+
DiscussionCategory: &github.DiscussionCategory{
138+
Name: github.Ptr(string(n.Category.Name)),
143139
},
144140
}
145141
discussions = append(discussions, di)
@@ -195,7 +191,6 @@ func GetDiscussion(getGQLClient GetGQLClientFn, t translations.TranslationHelper
195191
Discussion struct {
196192
Number githubv4.Int
197193
Body githubv4.String
198-
State githubv4.String
199194
CreatedAt githubv4.DateTime
200195
URL githubv4.String `graphql:"url"`
201196
Category struct {
@@ -213,16 +208,13 @@ func GetDiscussion(getGQLClient GetGQLClientFn, t translations.TranslationHelper
213208
return mcp.NewToolResultError(err.Error()), nil
214209
}
215210
d := q.Repository.Discussion
216-
discussion := &github.Issue{
211+
discussion := &github.Discussion{
217212
Number: github.Ptr(int(d.Number)),
218213
Body: github.Ptr(string(d.Body)),
219-
State: github.Ptr(string(d.State)),
220214
HTMLURL: github.Ptr(string(d.URL)),
221215
CreatedAt: &github.Timestamp{Time: d.CreatedAt.Time},
222-
Labels: []*github.Label{
223-
{
224-
Name: github.Ptr(fmt.Sprintf("category:%s", string(d.Category.Name))),
225-
},
216+
DiscussionCategory: &github.DiscussionCategory{
217+
Name: github.Ptr(string(d.Category.Name)),
226218
},
227219
}
228220
out, err := json.Marshal(discussion)

0 commit comments

Comments
 (0)