Skip to content

Commit 334a981

Browse files
committed
Split mock back out, use RepositoryCommit with Files/Stats
1 parent 06246a7 commit 334a981

23 files changed

+999
-371
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

README.md

Lines changed: 17 additions & 1 deletion
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

@@ -287,6 +288,17 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
287288
- `draft`: Create as draft PR (boolean, optional)
288289
- `maintainer_can_modify`: Allow maintainer edits (boolean, optional)
289290

291+
- **update_pull_request** - Update an existing pull request in a GitHub repository
292+
293+
- `owner`: Repository owner (string, required)
294+
- `repo`: Repository name (string, required)
295+
- `pullNumber`: Pull request number to update (number, required)
296+
- `title`: New title (string, optional)
297+
- `body`: New description (string, optional)
298+
- `state`: New state ('open' or 'closed') (string, optional)
299+
- `base`: New base branch name (string, optional)
300+
- `maintainer_can_modify`: Allow maintainer edits (boolean, optional)
301+
290302
### Repositories
291303

292304
- **create_or_update_file** - Create or update a single file in a repository
@@ -442,6 +454,10 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
442454
- `prNumber`: Pull request number (string, required)
443455
- `path`: File or directory path (string, optional)
444456

457+
## Library Usage
458+
459+
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.
460+
445461
## License
446462

447463
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: 4 additions & 24 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"
@@ -41,7 +39,6 @@ var (
4139
logFile := viper.GetString("log-file")
4240
readOnly := viper.GetBool("read-only")
4341
exportTranslations := viper.GetBool("export-translations")
44-
prettyPrintJSON := viper.GetBool("pretty-print-json")
4542
logger, err := initLogger(logFile)
4643
if err != nil {
4744
stdlog.Fatal("Failed to initialize logger:", err)
@@ -52,7 +49,6 @@ var (
5249
logger: logger,
5350
logCommands: logCommands,
5451
exportTranslations: exportTranslations,
55-
prettyPrintJSON: prettyPrintJSON,
5652
}
5753
if err := runStdioServer(cfg); err != nil {
5854
stdlog.Fatal("failed to run stdio server:", err)
@@ -70,15 +66,13 @@ func init() {
7066
rootCmd.PersistentFlags().Bool("enable-command-logging", false, "When enabled, the server will log all command requests and responses to the log file")
7167
rootCmd.PersistentFlags().Bool("export-translations", false, "Save translations to a JSON file")
7268
rootCmd.PersistentFlags().String("gh-host", "", "Specify the GitHub hostname (for GitHub Enterprise etc.)")
73-
rootCmd.PersistentFlags().Bool("pretty-print-json", false, "Pretty print JSON output")
7469

7570
// Bind flag to viper
7671
_ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only"))
7772
_ = viper.BindPFlag("log-file", rootCmd.PersistentFlags().Lookup("log-file"))
7873
_ = viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging"))
7974
_ = viper.BindPFlag("export-translations", rootCmd.PersistentFlags().Lookup("export-translations"))
8075
_ = viper.BindPFlag("gh-host", rootCmd.PersistentFlags().Lookup("gh-host"))
81-
_ = viper.BindPFlag("pretty-print-json", rootCmd.PersistentFlags().Lookup("pretty-print-json"))
8276

8377
// Add subcommands
8478
rootCmd.AddCommand(stdioCmd)
@@ -112,20 +106,6 @@ type runConfig struct {
112106
logger *log.Logger
113107
logCommands bool
114108
exportTranslations bool
115-
prettyPrintJSON bool
116-
}
117-
118-
// JSONPrettyPrintWriter is a Writer that pretty prints input to indented JSON
119-
type JSONPrettyPrintWriter struct {
120-
writer io.Writer
121-
}
122-
123-
func (j JSONPrettyPrintWriter) Write(p []byte) (n int, err error) {
124-
var prettyJSON bytes.Buffer
125-
if err := json.Indent(&prettyJSON, p, "", "\t"); err != nil {
126-
return 0, err
127-
}
128-
return j.writer.Write(prettyJSON.Bytes())
129109
}
130110

131111
func runStdioServer(cfg runConfig) error {
@@ -157,8 +137,11 @@ func runStdioServer(cfg runConfig) error {
157137

158138
t, dumpTranslations := translations.TranslationHelper()
159139

140+
getClient := func(_ context.Context) (*gogithub.Client, error) {
141+
return ghClient, nil // closing over client
142+
}
160143
// Create
161-
ghServer := github.NewServer(ghClient, version, cfg.readOnly, t)
144+
ghServer := github.NewServer(getClient, version, cfg.readOnly, t)
162145
stdioServer := server.NewStdioServer(ghServer)
163146

164147
stdLogger := stdlog.New(cfg.logger.Writer(), "stdioserver", 0)
@@ -179,9 +162,6 @@ func runStdioServer(cfg runConfig) error {
179162
in, out = loggedIO, loggedIO
180163
}
181164

182-
if cfg.prettyPrintJSON {
183-
out = JSONPrettyPrintWriter{writer: out}
184-
}
185165
errC <- stdioServer.Listen(ctx, in, out)
186166
}()
187167

cmd/mcpcurl/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Available Commands:
4949
create_repository Create a new GitHub repository in your account
5050
fork_repository Fork a GitHub repository to your account or specified organization
5151
get_file_contents Get the contents of a file or directory from a GitHub repository
52-
get_issue Get details of a specific issue in a GitHub repository.
52+
get_issue Get details of a specific issue in a GitHub repository
5353
get_issue_comments Get comments for a GitHub issue
5454
list_commits Get list of commits of a branch in a GitHub repository
5555
list_issues List issues in a GitHub repository with filtering options
@@ -74,7 +74,7 @@ Get help for a specific tool:
7474

7575
```bash
7676
% ./mcpcurl --stdio-server-cmd "docker run -i --rm -e GITHUB_PERSONAL_ACCESS_TOKEN mcp/github" tools get_issue --help
77-
Get details of a specific issue in a GitHub repository.
77+
Get details of a specific issue in a GitHub repository
7878

7979
Usage:
8080
mcpcurl tools get_issue [flags]

pkg/github/code_scanning.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/mark3labs/mcp-go/server"
1414
)
1515

16-
func getCodeScanningAlert(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
16+
func GetCodeScanningAlert(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
1717
return mcp.NewTool("get_code_scanning_alert",
1818
mcp.WithDescription(t("TOOL_GET_CODE_SCANNING_ALERT_DESCRIPTION", "Get details of a specific code scanning alert in a GitHub repository.")),
1919
mcp.WithString("owner",
@@ -38,11 +38,16 @@ func getCodeScanningAlert(client *github.Client, t translations.TranslationHelpe
3838
if err != nil {
3939
return mcp.NewToolResultError(err.Error()), nil
4040
}
41-
alertNumber, err := requiredInt(request, "alertNumber")
41+
alertNumber, err := RequiredInt(request, "alertNumber")
4242
if err != nil {
4343
return mcp.NewToolResultError(err.Error()), nil
4444
}
4545

46+
client, err := getClient(ctx)
47+
if err != nil {
48+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
49+
}
50+
4651
alert, resp, err := client.CodeScanning.GetAlert(ctx, owner, repo, int64(alertNumber))
4752
if err != nil {
4853
return nil, fmt.Errorf("failed to get alert: %w", err)
@@ -66,7 +71,7 @@ func getCodeScanningAlert(client *github.Client, t translations.TranslationHelpe
6671
}
6772
}
6873

69-
func listCodeScanningAlerts(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
74+
func ListCodeScanningAlerts(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
7075
return mcp.NewTool("list_code_scanning_alerts",
7176
mcp.WithDescription(t("TOOL_LIST_CODE_SCANNING_ALERTS_DESCRIPTION", "List code scanning alerts in a GitHub repository.")),
7277
mcp.WithString("owner",
@@ -97,19 +102,23 @@ func listCodeScanningAlerts(client *github.Client, t translations.TranslationHel
97102
if err != nil {
98103
return mcp.NewToolResultError(err.Error()), nil
99104
}
100-
ref, err := optionalParam[string](request, "ref")
105+
ref, err := OptionalParam[string](request, "ref")
101106
if err != nil {
102107
return mcp.NewToolResultError(err.Error()), nil
103108
}
104-
state, err := optionalParam[string](request, "state")
109+
state, err := OptionalParam[string](request, "state")
105110
if err != nil {
106111
return mcp.NewToolResultError(err.Error()), nil
107112
}
108-
severity, err := optionalParam[string](request, "severity")
113+
severity, err := OptionalParam[string](request, "severity")
109114
if err != nil {
110115
return mcp.NewToolResultError(err.Error()), nil
111116
}
112117

118+
client, err := getClient(ctx)
119+
if err != nil {
120+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
121+
}
113122
alerts, resp, err := client.CodeScanning.ListAlertsForRepo(ctx, owner, repo, &github.AlertListOptions{Ref: ref, State: state, Severity: severity})
114123
if err != nil {
115124
return nil, fmt.Errorf("failed to list alerts: %w", err)

pkg/github/code_scanning_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
func Test_GetCodeScanningAlert(t *testing.T) {
1717
// Verify tool definition once
1818
mockClient := github.NewClient(nil)
19-
tool, _ := getCodeScanningAlert(mockClient, translations.NullTranslationHelper)
19+
tool, _ := GetCodeScanningAlert(stubGetClientFn(mockClient), translations.NullTranslationHelper)
2020

2121
assert.Equal(t, "get_code_scanning_alert", tool.Name)
2222
assert.NotEmpty(t, tool.Description)
@@ -82,7 +82,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
8282
t.Run(tc.name, func(t *testing.T) {
8383
// Setup client with mock
8484
client := github.NewClient(tc.mockedClient)
85-
_, handler := getCodeScanningAlert(client, translations.NullTranslationHelper)
85+
_, handler := GetCodeScanningAlert(stubGetClientFn(client), translations.NullTranslationHelper)
8686

8787
// Create call request
8888
request := createMCPRequest(tc.requestArgs)
@@ -118,7 +118,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
118118
func Test_ListCodeScanningAlerts(t *testing.T) {
119119
// Verify tool definition once
120120
mockClient := github.NewClient(nil)
121-
tool, _ := listCodeScanningAlerts(mockClient, translations.NullTranslationHelper)
121+
tool, _ := ListCodeScanningAlerts(stubGetClientFn(mockClient), translations.NullTranslationHelper)
122122

123123
assert.Equal(t, "list_code_scanning_alerts", tool.Name)
124124
assert.NotEmpty(t, tool.Description)
@@ -201,7 +201,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
201201
t.Run(tc.name, func(t *testing.T) {
202202
// Setup client with mock
203203
client := github.NewClient(tc.mockedClient)
204-
_, handler := listCodeScanningAlerts(client, translations.NullTranslationHelper)
204+
_, handler := ListCodeScanningAlerts(stubGetClientFn(client), translations.NullTranslationHelper)
205205

206206
// Create call request
207207
request := createMCPRequest(tc.requestArgs)

0 commit comments

Comments
 (0)