Skip to content

Commit a3a4d53

Browse files
Merge branch 'main' into feat/version-subcommand
2 parents 106def2 + ff3036d commit a3a4d53

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3221
-823
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
@juruen @sammorrowdrums @williammartin @toby
1+
* @juruen @sammorrowdrums @williammartin @toby

.github/workflows/docker-publish.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ jobs:
6666
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
6767
with:
6868
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
69+
tags: |
70+
type=schedule
71+
type=ref,event=branch
72+
type=ref,event=tag
73+
type=ref,event=pr
74+
type=semver,pattern={{version}}
75+
type=semver,pattern={{major}}.{{minor}}
76+
type=semver,pattern={{major}}
77+
type=sha
78+
type=edge
79+
# Custom rule to prevent pre-releases from getting latest tag
80+
type=raw,value=latest,enable=${{ github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-') }}
6981
7082
- name: Go Build Cache for Docker
7183
uses: actions/cache@v4

.github/workflows/goreleaser.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
- "v*"
66
permissions:
77
contents: write
8+
id-token: write
9+
attestations: write
810

911
jobs:
1012
release:
@@ -33,3 +35,11 @@ jobs:
3335
workdir: .
3436
env:
3537
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Generate signed build provenance attestations for workflow artifacts
40+
uses: actions/attest-build-provenance@v2
41+
with:
42+
subject-path: |
43+
dist/*.tar.gz
44+
dist/*.zip
45+
dist/*.txt

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[fork]: https://github.com/github/github-mcp-server/fork
44
[pr]: https://github.com/github/github-mcp-server/compare
5-
[style]: https://github.com/github/github-mcp-server/blob/main/.golangci.yaml
5+
[style]: https://github.com/github/github-mcp-server/blob/main/.golangci.yml
66

77
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
88

README.md

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ automation and interaction capabilities for developers and tools.
1515
## Prerequisites
1616

1717
1. To run the server in a container, you will need to have [Docker](https://www.docker.com/) installed.
18-
2. [Create a GitHub Personal Access Token](https://github.com/settings/personal-access-tokens/new).
18+
2. Once Docker is installed, you will also need to ensure Docker is running.
19+
3. Lastly you will need to [Create a GitHub Personal Access Token](https://github.com/settings/personal-access-tokens/new).
1920
The MCP server can use many of the GitHub APIs, so enable the permissions that you feel comfortable granting your AI tools (to learn more about access tokens, please check out the [documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)).
2021

2122

@@ -90,10 +91,24 @@ More about using MCP server tools in VS Code's [agent mode documentation](https:
9091

9192
### Build from source
9293

93-
If you don't have Docker, you can use `go` to build the binary in the
94-
`cmd/github-mcp-server` directory, and use the `github-mcp-server stdio`
95-
command with the `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable set to
96-
your token.
94+
If you don't have Docker, you can use `go build` to build the binary in the
95+
`cmd/github-mcp-server` directory, and use the `github-mcp-server stdio` command with the `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable set to your token. To specify the output location of the build, use the `-o` flag. You should configure your server to use the built executable as its `command`. For example:
96+
97+
```JSON
98+
{
99+
"mcp": {
100+
"servers": {
101+
"github": {
102+
"command": "/path/to/github-mcp-server",
103+
"args": ["stdio"],
104+
"env": {
105+
"GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
106+
}
107+
}
108+
}
109+
}
110+
}
111+
```
97112

98113
## GitHub Enterprise Server
99114

@@ -153,6 +168,12 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
153168
- `repo`: Repository name (string, required)
154169
- `issue_number`: Issue number (number, required)
155170

171+
- **get_issue_comments** - Get comments for a GitHub issue
172+
173+
- `owner`: Repository owner (string, required)
174+
- `repo`: Repository name (string, required)
175+
- `issue_number`: Issue number (number, required)
176+
156177
- **create_issue** - Create a new issue in a GitHub repository
157178

158179
- `owner`: Repository owner (string, required)
@@ -198,7 +219,7 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
198219
- `sort`: Sort field (string, optional)
199220
- `order`: Sort order (string, optional)
200221
- `page`: Page number (number, optional)
201-
- `per_page`: Results per page (number, optional)
222+
- `perPage`: Results per page (number, optional)
202223

203224
### Pull Requests
204225

@@ -266,7 +287,9 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
266287
- `body`: Review comment text (string, optional)
267288
- `event`: Review action ('APPROVE', 'REQUEST_CHANGES', 'COMMENT') (string, required)
268289
- `commitId`: SHA of commit to review (string, optional)
269-
- `comments`: Line-specific comments array of objects, each object with path (string), position (number), and body (string) (array, optional)
290+
- `comments`: Line-specific comments array of objects to place comments on pull request changes (array, optional)
291+
- For inline comments: provide `path`, `position` (or `line`), and `body`
292+
- For multi-line comments: provide `path`, `start_line`, `line`, optional `side`/`start_side`, and `body`
270293

271294
- **create_pull_request** - Create a new pull request
272295

@@ -279,6 +302,32 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
279302
- `draft`: Create as draft PR (boolean, optional)
280303
- `maintainer_can_modify`: Allow maintainer edits (boolean, optional)
281304

305+
- **add_pull_request_review_comment** - Add a review comment to a pull request or reply to an existing comment
306+
307+
- `owner`: Repository owner (string, required)
308+
- `repo`: Repository name (string, required)
309+
- `pull_number`: Pull request number (number, required)
310+
- `body`: The text of the review comment (string, required)
311+
- `commit_id`: The SHA of the commit to comment on (string, required unless using in_reply_to)
312+
- `path`: The relative path to the file that necessitates a comment (string, required unless using in_reply_to)
313+
- `line`: The line of the blob in the pull request diff that the comment applies to (number, optional)
314+
- `side`: The side of the diff to comment on (LEFT or RIGHT) (string, optional)
315+
- `start_line`: For multi-line comments, the first line of the range (number, optional)
316+
- `start_side`: For multi-line comments, the starting side of the diff (LEFT or RIGHT) (string, optional)
317+
- `subject_type`: The level at which the comment is targeted (line or file) (string, optional)
318+
- `in_reply_to`: The ID of the review comment to reply to (number, optional). When specified, only body is required and other parameters are ignored.
319+
320+
- **update_pull_request** - Update an existing pull request in a GitHub repository
321+
322+
- `owner`: Repository owner (string, required)
323+
- `repo`: Repository name (string, required)
324+
- `pullNumber`: Pull request number to update (number, required)
325+
- `title`: New title (string, optional)
326+
- `body`: New description (string, optional)
327+
- `state`: New state ('open' or 'closed') (string, optional)
328+
- `base`: New base branch name (string, optional)
329+
- `maintainer_can_modify`: Allow maintainer edits (boolean, optional)
330+
282331
### Repositories
283332

284333
- **create_or_update_file** - Create or update a single file in a repository
@@ -291,6 +340,13 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
291340
- `branch`: Branch name (string, optional)
292341
- `sha`: File SHA if updating (string, optional)
293342

343+
- **list_branches** - List branches in a GitHub repository
344+
345+
- `owner`: Repository owner (string, required)
346+
- `repo`: Repository name (string, required)
347+
- `page`: Page number (number, optional)
348+
- `perPage`: Results per page (number, optional)
349+
294350
- **push_files** - Push multiple files in a single commit
295351

296352
- `owner`: Repository owner (string, required)
@@ -334,14 +390,21 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
334390
- `branch`: New branch name (string, required)
335391
- `sha`: SHA to create branch from (string, required)
336392

337-
- **list_commits** - Gets commits of a branch in a repository
393+
- **list_commits** - Get a list of commits of a branch in a repository
338394
- `owner`: Repository owner (string, required)
339395
- `repo`: Repository name (string, required)
340396
- `sha`: Branch name, tag, or commit SHA (string, optional)
341397
- `path`: Only commits containing this file path (string, optional)
342398
- `page`: Page number (number, optional)
343399
- `perPage`: Results per page (number, optional)
344400

401+
- **get_commit** - Get details for a commit from a repository
402+
- `owner`: Repository owner (string, required)
403+
- `repo`: Repository name (string, required)
404+
- `sha`: Commit SHA, branch name, or tag name (string, required)
405+
- `page`: Page number, for files in the commit (number, optional)
406+
- `perPage`: Results per page, for files in the commit (number, optional)
407+
345408
### Search
346409

347410
- **search_code** - Search for code across GitHub repositories
@@ -427,6 +490,10 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
427490
- `prNumber`: Pull request number (string, required)
428491
- `path`: File or directory path (string, optional)
429492

493+
## Library Usage
494+
495+
The exported Go API of this module should currently be considered unstable, and subject to breaking changes. In the future, we may offer stability; please file an issue if there is a use case where this would be valuable.
496+
430497
## License
431498

432499
This project is licensed under the terms of the MIT open source license. Please refer to [MIT](./LICENSE) for the full terms.

cmd/github-mcp-server/main.go

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package main
22

33
import (
4-
"bytes"
54
"context"
6-
"encoding/json"
75
"fmt"
86
"io"
97
stdlog "log"
@@ -15,6 +13,7 @@ import (
1513
iolog "github.com/github/github-mcp-server/pkg/log"
1614
"github.com/github/github-mcp-server/pkg/translations"
1715
gogithub "github.com/google/go-github/v69/github"
16+
"github.com/mark3labs/mcp-go/mcp"
1817
"github.com/mark3labs/mcp-go/server"
1918
log "github.com/sirupsen/logrus"
2019
"github.com/spf13/cobra"
@@ -41,18 +40,20 @@ var (
4140
logFile := viper.GetString("log-file")
4241
readOnly := viper.GetBool("read-only")
4342
exportTranslations := viper.GetBool("export-translations")
44-
prettyPrintJSON := viper.GetBool("pretty-print-json")
4543
logger, err := initLogger(logFile)
4644
if err != nil {
4745
stdlog.Fatal("Failed to initialize logger:", err)
4846
}
47+
48+
enabledToolsets := viper.GetStringSlice("toolsets")
49+
4950
logCommands := viper.GetBool("enable-command-logging")
5051
cfg := runConfig{
5152
readOnly: readOnly,
5253
logger: logger,
5354
logCommands: logCommands,
5455
exportTranslations: exportTranslations,
55-
prettyPrintJSON: prettyPrintJSON,
56+
enabledToolsets: enabledToolsets,
5657
}
5758
if err := runStdioServer(cfg); err != nil {
5859
stdlog.Fatal("failed to run stdio server:", err)
@@ -67,28 +68,30 @@ func init() {
6768
rootCmd.SetVersionTemplate("{{.Short}}\n{{.Version}}\n")
6869

6970
// Add global flags that will be shared by all commands
71+
rootCmd.PersistentFlags().StringSlice("toolsets", github.DefaultTools, "An optional comma separated list of groups of tools to allow, defaults to enabling all")
72+
rootCmd.PersistentFlags().Bool("dynamic-toolsets", false, "Enable dynamic toolsets")
7073
rootCmd.PersistentFlags().Bool("read-only", false, "Restrict the server to read-only operations")
7174
rootCmd.PersistentFlags().String("log-file", "", "Path to log file")
7275
rootCmd.PersistentFlags().Bool("enable-command-logging", false, "When enabled, the server will log all command requests and responses to the log file")
7376
rootCmd.PersistentFlags().Bool("export-translations", false, "Save translations to a JSON file")
7477
rootCmd.PersistentFlags().String("gh-host", "", "Specify the GitHub hostname (for GitHub Enterprise etc.)")
75-
rootCmd.PersistentFlags().Bool("pretty-print-json", false, "Pretty print JSON output")
7678

7779
// Bind flag to viper
80+
_ = viper.BindPFlag("toolsets", rootCmd.PersistentFlags().Lookup("toolsets"))
81+
_ = viper.BindPFlag("dynamic_toolsets", rootCmd.PersistentFlags().Lookup("dynamic-toolsets"))
7882
_ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only"))
7983
_ = viper.BindPFlag("log-file", rootCmd.PersistentFlags().Lookup("log-file"))
8084
_ = viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging"))
8185
_ = viper.BindPFlag("export-translations", rootCmd.PersistentFlags().Lookup("export-translations"))
82-
_ = viper.BindPFlag("gh-host", rootCmd.PersistentFlags().Lookup("gh-host"))
83-
_ = viper.BindPFlag("pretty-print-json", rootCmd.PersistentFlags().Lookup("pretty-print-json"))
86+
_ = viper.BindPFlag("host", rootCmd.PersistentFlags().Lookup("gh-host"))
8487

8588
// Add subcommands
8689
rootCmd.AddCommand(stdioCmd)
8790
}
8891

8992
func initConfig() {
9093
// Initialize Viper configuration
91-
viper.SetEnvPrefix("APP")
94+
viper.SetEnvPrefix("github")
9295
viper.AutomaticEnv()
9396
}
9497

@@ -114,20 +117,7 @@ type runConfig struct {
114117
logger *log.Logger
115118
logCommands bool
116119
exportTranslations bool
117-
prettyPrintJSON bool
118-
}
119-
120-
// JSONPrettyPrintWriter is a Writer that pretty prints input to indented JSON
121-
type JSONPrettyPrintWriter struct {
122-
writer io.Writer
123-
}
124-
125-
func (j JSONPrettyPrintWriter) Write(p []byte) (n int, err error) {
126-
var prettyJSON bytes.Buffer
127-
if err := json.Indent(&prettyJSON, p, "", "\t"); err != nil {
128-
return 0, err
129-
}
130-
return j.writer.Write(prettyJSON.Bytes())
120+
enabledToolsets []string
131121
}
132122

133123
func runStdioServer(cfg runConfig) error {
@@ -136,18 +126,14 @@ func runStdioServer(cfg runConfig) error {
136126
defer stop()
137127

138128
// Create GH client
139-
token := os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN")
129+
token := viper.GetString("personal_access_token")
140130
if token == "" {
141131
cfg.logger.Fatal("GITHUB_PERSONAL_ACCESS_TOKEN not set")
142132
}
143133
ghClient := gogithub.NewClient(nil).WithAuthToken(token)
144134
ghClient.UserAgent = fmt.Sprintf("github-mcp-server/%s", version)
145135

146-
// Check GH_HOST env var first, then fall back to viper config
147-
host := os.Getenv("GH_HOST")
148-
if host == "" {
149-
host = viper.GetString("gh-host")
150-
}
136+
host := viper.GetString("host")
151137

152138
if host != "" {
153139
var err error
@@ -159,8 +145,51 @@ func runStdioServer(cfg runConfig) error {
159145

160146
t, dumpTranslations := translations.TranslationHelper()
161147

162-
// Create
163-
ghServer := github.NewServer(ghClient, cfg.readOnly, t)
148+
beforeInit := func(_ context.Context, _ any, message *mcp.InitializeRequest) {
149+
ghClient.UserAgent = fmt.Sprintf("github-mcp-server/%s (%s/%s)", version, message.Params.ClientInfo.Name, message.Params.ClientInfo.Version)
150+
}
151+
152+
getClient := func(_ context.Context) (*gogithub.Client, error) {
153+
return ghClient, nil // closing over client
154+
}
155+
156+
hooks := &server.Hooks{
157+
OnBeforeInitialize: []server.OnBeforeInitializeFunc{beforeInit},
158+
}
159+
// Create server
160+
ghServer := github.NewServer(version, server.WithHooks(hooks))
161+
162+
enabled := cfg.enabledToolsets
163+
dynamic := viper.GetBool("dynamic_toolsets")
164+
if dynamic {
165+
// filter "all" from the enabled toolsets
166+
enabled = make([]string, 0, len(cfg.enabledToolsets))
167+
for _, toolset := range cfg.enabledToolsets {
168+
if toolset != "all" {
169+
enabled = append(enabled, toolset)
170+
}
171+
}
172+
}
173+
174+
// Create default toolsets
175+
toolsets, err := github.InitToolsets(enabled, cfg.readOnly, getClient, t)
176+
context := github.InitContextToolset(getClient, t)
177+
178+
if err != nil {
179+
stdlog.Fatal("Failed to initialize toolsets:", err)
180+
}
181+
182+
// Register resources with the server
183+
github.RegisterResources(ghServer, getClient, t)
184+
// Register the tools with the server
185+
toolsets.RegisterTools(ghServer)
186+
context.RegisterTools(ghServer)
187+
188+
if dynamic {
189+
dynamic := github.InitDynamicToolset(ghServer, toolsets, t)
190+
dynamic.RegisterTools(ghServer)
191+
}
192+
164193
stdioServer := server.NewStdioServer(ghServer)
165194

166195
stdLogger := stdlog.New(cfg.logger.Writer(), "stdioserver", 0)
@@ -181,9 +210,6 @@ func runStdioServer(cfg runConfig) error {
181210
in, out = loggedIO, loggedIO
182211
}
183212

184-
if cfg.prettyPrintJSON {
185-
out = JSONPrettyPrintWriter{writer: out}
186-
}
187213
errC <- stdioServer.Listen(ctx, in, out)
188214
}()
189215

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