Skip to content

Commit 954dac5

Browse files
Merge branch 'main' into read-only
2 parents 0c378c8 + dd46f8a commit 954dac5

File tree

17 files changed

+1032
-131
lines changed

17 files changed

+1032
-131
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
name: Docker
2+
3+
# This workflow uses actions that are not certified by GitHub.
4+
# They are provided by a third-party and are governed by
5+
# separate terms of service, privacy policy, and support
6+
# documentation.
7+
8+
on:
9+
schedule:
10+
- cron: '27 0 * * *'
11+
push:
12+
branches: [ "main" ]
13+
# Publish semver tags as releases.
14+
tags: [ 'v*.*.*' ]
15+
pull_request:
16+
branches: [ "main" ]
17+
18+
env:
19+
# Use docker.io for Docker Hub if empty
20+
REGISTRY: ghcr.io
21+
# github.repository as <account>/<repo>
22+
IMAGE_NAME: ${{ github.repository }}
23+
24+
25+
jobs:
26+
build:
27+
28+
runs-on: ubuntu-latest
29+
permissions:
30+
contents: read
31+
packages: write
32+
# This is used to complete the identity challenge
33+
# with sigstore/fulcio when running outside of PRs.
34+
id-token: write
35+
36+
steps:
37+
- name: Checkout repository
38+
uses: actions/checkout@v4
39+
40+
# Install the cosign tool except on PR
41+
# https://github.com/sigstore/cosign-installer
42+
- name: Install cosign
43+
if: github.event_name != 'pull_request'
44+
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0
45+
with:
46+
cosign-release: 'v2.2.4'
47+
48+
# Set up BuildKit Docker container builder to be able to build
49+
# multi-platform images and export cache
50+
# https://github.com/docker/setup-buildx-action
51+
- name: Set up Docker Buildx
52+
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
53+
54+
# Login against a Docker registry except on PR
55+
# https://github.com/docker/login-action
56+
- name: Log into registry ${{ env.REGISTRY }}
57+
if: github.event_name != 'pull_request'
58+
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
59+
with:
60+
registry: ${{ env.REGISTRY }}
61+
username: ${{ github.actor }}
62+
password: ${{ secrets.GITHUB_TOKEN }}
63+
64+
# Extract metadata (tags, labels) for Docker
65+
# https://github.com/docker/metadata-action
66+
- name: Extract Docker metadata
67+
id: meta
68+
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
69+
with:
70+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
71+
72+
# Build and push Docker image with Buildx (don't push on PR)
73+
# https://github.com/docker/build-push-action
74+
- name: Build and push Docker image
75+
id: build-and-push
76+
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
77+
with:
78+
context: .
79+
push: ${{ github.event_name != 'pull_request' }}
80+
tags: ${{ steps.meta.outputs.tags }}
81+
labels: ${{ steps.meta.outputs.labels }}
82+
cache-from: type=gha
83+
cache-to: type=gha,mode=max
84+
85+
# Sign the resulting Docker image digest except on PRs.
86+
# This will only write to the public Rekor transparency log when the Docker
87+
# repository is public to avoid leaking data. If you would like to publish
88+
# transparency data even for private images, pass --force to cosign below.
89+
# https://github.com/sigstore/cosign
90+
- name: Sign the published Docker image
91+
if: ${{ github.event_name != 'pull_request' }}
92+
env:
93+
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
94+
TAGS: ${{ steps.meta.outputs.tags }}
95+
DIGEST: ${{ steps.build-and-push.outputs.digest }}
96+
# This step uses the identity token to provision an ephemeral certificate
97+
# against the sigstore community Fulcio instance.
98+
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}

README.md

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,34 @@ and set it as the GITHUB_PERSONAL_ACCESS_TOKEN environment variable.
2222
- `repo`: Repository name (string, required)
2323
- `issue_number`: Issue number (number, required)
2424

25+
- **create_issue** - Create a new issue in a GitHub repository
26+
27+
- `owner`: Repository owner (string, required)
28+
- `repo`: Repository name (string, required)
29+
- `title`: Issue title (string, required)
30+
- `body`: Issue body content (string, optional)
31+
- `assignees`: Comma-separated list of usernames to assign to this issue (string, optional)
32+
- `labels`: Comma-separated list of labels to apply to this issue (string, optional)
33+
2534
- **add_issue_comment** - Add a comment to an issue
2635

2736
- `owner`: Repository owner (string, required)
2837
- `repo`: Repository name (string, required)
2938
- `issue_number`: Issue number (number, required)
3039
- `body`: Comment text (string, required)
3140

41+
- **list_issues** - List and filter repository issues
42+
43+
- `owner`: Repository owner (string, required)
44+
- `repo`: Repository name (string, required)
45+
- `state`: Filter by state ('open', 'closed', 'all') (string, optional)
46+
- `labels`: Comma-separated list of labels to filter by (string, optional)
47+
- `sort`: Sort by ('created', 'updated', 'comments') (string, optional)
48+
- `direction`: Sort direction ('asc', 'desc') (string, optional)
49+
- `since`: Filter by date (ISO 8601 timestamp) (string, optional)
50+
- `page`: Page number (number, optional)
51+
- `per_page`: Results per page (number, optional)
52+
3253
- **search_issues** - Search for issues and pull requests
3354
- `query`: Search query (string, required)
3455
- `sort`: Sort field (string, optional)
@@ -267,6 +288,36 @@ GitHub MCP Server running on stdio
267288
268289
```
269290
291+
## i18n / Overriding descriptions
292+
293+
The descriptions of the tools can be overridden by creating a github-mcp-server.json file in the same directory as the binary.
294+
The file should contain a JSON object with the tool names as keys and the new descriptions as values.
295+
For example:
296+
297+
```json
298+
{
299+
"TOOL_ADD_ISSUE_COMMENT_DESCRIPTION": "an alternative description",
300+
"TOOL_CREATE_BRANCH_DESCRIPTION": "Create a new branch in a GitHub repository"
301+
}
302+
```
303+
304+
You can create an export of the current translations by running the binary with the `--export-translations` flag.
305+
This flag will preserve any translations/overrides you have made, while adding any new translations that have been added to the binary since the last time you exported.
306+
307+
```sh
308+
./github-mcp-server --export-translations
309+
cat github-mcp-server.json
310+
```
311+
312+
You can also use ENV vars to override the descriptions. The environment variable names are the same as the keys in the JSON file,
313+
prefixed with `GITHUB_MCP_` and all uppercase.
314+
315+
For example, to override the `TOOL_ADD_ISSUE_COMMENT_DESCRIPTION` tool, you can set the following environment variable:
316+
317+
```sh
318+
export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description"
319+
```
320+
270321
## Testing on VS Code Insiders
271322
272323
First of all, install `github-mcp-server` with:
@@ -313,16 +364,14 @@ Lots of things!
313364
Missing tools:
314365
315366
- push_files (files array)
316-
- create_issue (assignees and labels arrays)
317367
- list_issues (labels array)
318368
- update_issue (labels and assignees arrays)
319369
- create_pull_request_review (comments array)
320370
321371
Testing
322372
323-
- Unit tests
324373
- Integration tests
325-
- Blackbox testing: ideally comparing output to Anthromorphic's server to make sure that this is a fully compatible drop-in replacement.
374+
- Blackbox testing: ideally comparing output to Anthropic's server to make sure that this is a fully compatible drop-in replacement.
326375
327376
And some other stuff:
328377

cmd/github-mcp-server/main.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/github/github-mcp-server/pkg/github"
1313
iolog "github.com/github/github-mcp-server/pkg/log"
14+
"github.com/github/github-mcp-server/pkg/translations"
1415
gogithub "github.com/google/go-github/v69/github"
1516
"github.com/mark3labs/mcp-go/server"
1617
log "github.com/sirupsen/logrus"
@@ -37,7 +38,7 @@ var (
3738
stdlog.Fatal("Failed to initialize logger:", err)
3839
}
3940
logCommands := viper.GetBool("enable-command-logging")
40-
if err := runStdioServer(readOnly, logger, logCommands); err != nil {
41+
if err := runStdioServer(readOnly, logger, logCommands, viper.GetBool("export-translations")); err != nil {
4142
stdlog.Fatal("failed to run stdio server:", err)
4243
}
4344
},
@@ -51,11 +52,13 @@ func init() {
5152
rootCmd.PersistentFlags().Bool("read-only", false, "Restrict the server to read-only operations")
5253
rootCmd.PersistentFlags().String("log-file", "", "Path to log file")
5354
rootCmd.PersistentFlags().Bool("enable-command-logging", false, "When enabled, the server will log all command requests and responses to the log file")
55+
rootCmd.PersistentFlags().Bool("export-translations", false, "Save translations to a JSON file")
5456

5557
// Bind flag to viper
5658
viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only"))
5759
viper.BindPFlag("log-file", rootCmd.PersistentFlags().Lookup("log-file"))
5860
viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging"))
61+
viper.BindPFlag("export-translations", rootCmd.PersistentFlags().Lookup("export-translations"))
5962

6063
// Add subcommands
6164
rootCmd.AddCommand(stdioCmd)
@@ -84,7 +87,7 @@ func initLogger(outPath string) (*log.Logger, error) {
8487
return logger, nil
8588
}
8689

87-
func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool) error {
90+
func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportTranslations bool) error {
8891
// Create app context
8992
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
9093
defer stop()
@@ -96,13 +99,20 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool) error {
9699
}
97100
ghClient := gogithub.NewClient(nil).WithAuthToken(token)
98101

99-
// Create
100-
ghServer := github.NewServer(ghClient, readOnly)
102+
t, dumpTranslations := translations.TranslationHelper()
103+
104+
// Create server
105+
ghServer := github.NewServer(ghClient, readOnly, t)
101106
stdioServer := server.NewStdioServer(ghServer)
102107

103108
stdLogger := stdlog.New(logger.Writer(), "stdioserver", 0)
104109
stdioServer.SetErrorLogger(stdLogger)
105110

111+
if exportTranslations {
112+
// Once server is initialized, all translations are loaded
113+
dumpTranslations()
114+
}
115+
106116
// Start listening for messages
107117
errC := make(chan error, 1)
108118
go func() {

pkg/github/code_scanning.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import (
77
"io"
88
"net/http"
99

10+
"github.com/github/github-mcp-server/pkg/translations"
1011
"github.com/google/go-github/v69/github"
1112
"github.com/mark3labs/mcp-go/mcp"
1213
"github.com/mark3labs/mcp-go/server"
1314
)
1415

15-
func getCodeScanningAlert(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
16+
func getCodeScanningAlert(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
1617
return mcp.NewTool("get_code_scanning_alert",
17-
mcp.WithDescription("Get details of a specific code scanning alert in a GitHub repository."),
18+
mcp.WithDescription(t("TOOL_GET_CODE_SCANNING_ALERT_DESCRIPTION", "Get details of a specific code scanning alert in a GitHub repository.")),
1819
mcp.WithString("owner",
1920
mcp.Required(),
2021
mcp.Description("The owner of the repository."),
@@ -56,9 +57,9 @@ func getCodeScanningAlert(client *github.Client) (tool mcp.Tool, handler server.
5657
}
5758
}
5859

59-
func listCodeScanningAlerts(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
60+
func listCodeScanningAlerts(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
6061
return mcp.NewTool("list_code_scanning_alerts",
61-
mcp.WithDescription("List code scanning alerts in a GitHub repository."),
62+
mcp.WithDescription(t("TOOL_LIST_CODE_SCANNING_ALERTS_DESCRIPTION", "List code scanning alerts in a GitHub repository.")),
6263
mcp.WithString("owner",
6364
mcp.Required(),
6465
mcp.Description("The owner of the repository."),

pkg/github/code_scanning_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"testing"
88

9+
"github.com/github/github-mcp-server/pkg/translations"
910
"github.com/google/go-github/v69/github"
1011
"github.com/migueleliasweb/go-github-mock/src/mock"
1112
"github.com/stretchr/testify/assert"
@@ -15,7 +16,7 @@ import (
1516
func Test_GetCodeScanningAlert(t *testing.T) {
1617
// Verify tool definition once
1718
mockClient := github.NewClient(nil)
18-
tool, _ := getCodeScanningAlert(mockClient)
19+
tool, _ := getCodeScanningAlert(mockClient, translations.NullTranslationHelper)
1920

2021
assert.Equal(t, "get_code_scanning_alert", tool.Name)
2122
assert.NotEmpty(t, tool.Description)
@@ -81,7 +82,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
8182
t.Run(tc.name, func(t *testing.T) {
8283
// Setup client with mock
8384
client := github.NewClient(tc.mockedClient)
84-
_, handler := getCodeScanningAlert(client)
85+
_, handler := getCodeScanningAlert(client, translations.NullTranslationHelper)
8586

8687
// Create call request
8788
request := createMCPRequest(tc.requestArgs)
@@ -117,7 +118,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
117118
func Test_ListCodeScanningAlerts(t *testing.T) {
118119
// Verify tool definition once
119120
mockClient := github.NewClient(nil)
120-
tool, _ := listCodeScanningAlerts(mockClient)
121+
tool, _ := listCodeScanningAlerts(mockClient, translations.NullTranslationHelper)
121122

122123
assert.Equal(t, "list_code_scanning_alerts", tool.Name)
123124
assert.NotEmpty(t, tool.Description)
@@ -194,7 +195,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
194195
t.Run(tc.name, func(t *testing.T) {
195196
// Setup client with mock
196197
client := github.NewClient(tc.mockedClient)
197-
_, handler := listCodeScanningAlerts(client)
198+
_, handler := listCodeScanningAlerts(client, translations.NullTranslationHelper)
198199

199200
// Create call request
200201
request := createMCPRequest(tc.requestArgs)

0 commit comments

Comments
 (0)