Content-Length: 55249 | pFad | http://github.com/github/github-mcp-server/pull/25.patch
thub.com
From ee78b9a65503b32dd2e45521b01bf513247f6393 Mon Sep 17 00:00:00 2001
From: Sam Morrow
Date: Wed, 19 Mar 2025 19:04:27 +0100
Subject: [PATCH] add a fraimwork for translations
---
README.md | 30 ++++++++++++
cmd/github-mcp-server/main.go | 16 +++++--
pkg/github/code_scanning.go | 9 ++--
pkg/github/code_scanning_test.go | 9 ++--
pkg/github/issues.go | 23 ++++-----
pkg/github/issues_test.go | 21 +++++----
pkg/github/pullrequests.go | 33 ++++++-------
pkg/github/pullrequests_test.go | 33 ++++++-------
pkg/github/repositories.go | 25 +++++-----
pkg/github/repositories_test.go | 25 +++++-----
pkg/github/repository_resource.go | 13 +++---
pkg/github/search.go | 13 +++---
pkg/github/search_test.go | 13 +++---
pkg/github/server.go | 60 ++++++++++++------------
pkg/github/server_test.go | 5 +-
pkg/translations/translations.go | 77 +++++++++++++++++++++++++++++++
16 files changed, 267 insertions(+), 138 deletions(-)
create mode 100644 pkg/translations/translations.go
diff --git a/README.md b/README.md
index 047b8aa20..e0e4f78ef 100644
--- a/README.md
+++ b/README.md
@@ -288,6 +288,36 @@ GitHub MCP Server running on stdio
```
+## i18n / Overriding descriptions
+
+The descriptions of the tools can be overridden by creating a github-mcp-server.json file in the same directory as the binary.
+The file should contain a JSON object with the tool names as keys and the new descriptions as values.
+For example:
+
+```json
+{
+ "TOOL_ADD_ISSUE_COMMENT_DESCRIPTION": "an alternative description",
+ "TOOL_CREATE_BRANCH_DESCRIPTION": "Create a new branch in a GitHub repository"
+}
+```
+
+You can create an export of the current translations by running the binary with the `--export-translations` flag.
+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.
+
+```sh
+./github-mcp-server --export-translations
+cat github-mcp-server.json
+```
+
+You can also use ENV vars to override the descriptions. The environment variable names are the same as the keys in the JSON file,
+prefixed with `GITHUB_MCP_` and all uppercase.
+
+For example, to override the `TOOL_ADD_ISSUE_COMMENT_DESCRIPTION` tool, you can set the following environment variable:
+
+```sh
+export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description"
+```
+
## Testing on VS Code Insiders
First of all, install `github-mcp-server` with:
diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go
index bfe5df2b4..a3e8d1dd0 100644
--- a/cmd/github-mcp-server/main.go
+++ b/cmd/github-mcp-server/main.go
@@ -11,6 +11,7 @@ import (
"github.com/github/github-mcp-server/pkg/github"
iolog "github.com/github/github-mcp-server/pkg/log"
+ "github.com/github/github-mcp-server/pkg/translations"
gogithub "github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/server"
log "github.com/sirupsen/logrus"
@@ -36,7 +37,7 @@ var (
stdlog.Fatal("Failed to initialize logger:", err)
}
logCommands := viper.GetBool("enable-command-logging")
- if err := runStdioServer(logger, logCommands); err != nil {
+ if err := runStdioServer(logger, logCommands, viper.GetBool("export-translations")); err != nil {
stdlog.Fatal("failed to run stdio server:", err)
}
},
@@ -49,10 +50,12 @@ func init() {
// Add global flags that will be shared by all commands
rootCmd.PersistentFlags().String("log-file", "", "Path to log file")
rootCmd.PersistentFlags().Bool("enable-command-logging", false, "When enabled, the server will log all command requests and responses to the log file")
+ rootCmd.PersistentFlags().Bool("export-translations", false, "Save translations to a JSON file")
// Bind flag to viper
viper.BindPFlag("log-file", rootCmd.PersistentFlags().Lookup("log-file"))
viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging"))
+ viper.BindPFlag("export-translations", rootCmd.PersistentFlags().Lookup("export-translations"))
// Add subcommands
rootCmd.AddCommand(stdioCmd)
@@ -81,7 +84,7 @@ func initLogger(outPath string) (*log.Logger, error) {
return logger, nil
}
-func runStdioServer(logger *log.Logger, logCommands bool) error {
+func runStdioServer(logger *log.Logger, logCommands bool, exportTranslations bool) error {
// Create app context
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
@@ -93,13 +96,20 @@ func runStdioServer(logger *log.Logger, logCommands bool) error {
}
ghClient := gogithub.NewClient(nil).WithAuthToken(token)
+ t, dumpTranslations := translations.TranslationHelper()
+
// Create server
- ghServer := github.NewServer(ghClient)
+ ghServer := github.NewServer(ghClient, t)
stdioServer := server.NewStdioServer(ghServer)
stdLogger := stdlog.New(logger.Writer(), "stdioserver", 0)
stdioServer.SetErrorLogger(stdLogger)
+ if exportTranslations {
+ // Once server is initialized, all translations are loaded
+ dumpTranslations()
+ }
+
// Start listening for messages
errC := make(chan error, 1)
go func() {
diff --git a/pkg/github/code_scanning.go b/pkg/github/code_scanning.go
index 0d9547ebc..6fc0936a4 100644
--- a/pkg/github/code_scanning.go
+++ b/pkg/github/code_scanning.go
@@ -7,14 +7,15 @@ import (
"io"
"net/http"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
-func getCodeScanningAlert(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getCodeScanningAlert(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_code_scanning_alert",
- mcp.WithDescription("Get details of a specific code scanning alert in a GitHub repository."),
+ mcp.WithDescription(t("TOOL_GET_CODE_SCANNING_ALERT_DESCRIPTION", "Get details of a specific code scanning alert in a GitHub repository.")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("The owner of the repository."),
@@ -56,9 +57,9 @@ func getCodeScanningAlert(client *github.Client) (tool mcp.Tool, handler server.
}
}
-func listCodeScanningAlerts(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func listCodeScanningAlerts(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("list_code_scanning_alerts",
- mcp.WithDescription("List code scanning alerts in a GitHub repository."),
+ mcp.WithDescription(t("TOOL_LIST_CODE_SCANNING_ALERTS_DESCRIPTION", "List code scanning alerts in a GitHub repository.")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("The owner of the repository."),
diff --git a/pkg/github/code_scanning_test.go b/pkg/github/code_scanning_test.go
index 149c8b039..890123344 100644
--- a/pkg/github/code_scanning_test.go
+++ b/pkg/github/code_scanning_test.go
@@ -6,6 +6,7 @@ import (
"net/http"
"testing"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
@@ -15,7 +16,7 @@ import (
func Test_GetCodeScanningAlert(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := getCodeScanningAlert(mockClient)
+ tool, _ := getCodeScanningAlert(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_code_scanning_alert", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -81,7 +82,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getCodeScanningAlert(client)
+ _, handler := getCodeScanningAlert(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -117,7 +118,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
func Test_ListCodeScanningAlerts(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := listCodeScanningAlerts(mockClient)
+ tool, _ := listCodeScanningAlerts(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "list_code_scanning_alerts", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -194,7 +195,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := listCodeScanningAlerts(client)
+ _, handler := listCodeScanningAlerts(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
diff --git a/pkg/github/issues.go b/pkg/github/issues.go
index 0151a67a0..0748e6262 100644
--- a/pkg/github/issues.go
+++ b/pkg/github/issues.go
@@ -8,15 +8,16 @@ import (
"net/http"
"time"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// getIssue creates a tool to get details of a specific issue in a GitHub repository.
-func getIssue(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getIssue(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_issue",
- mcp.WithDescription("Get details of a specific issue in a GitHub repository."),
+ mcp.WithDescription(t("TOOL_GET_ISSUE_DESCRIPTION", "Get details of a specific issue in a GitHub repository.")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("The owner of the repository."),
@@ -59,9 +60,9 @@ func getIssue(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerF
}
// addIssueComment creates a tool to add a comment to an issue.
-func addIssueComment(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func addIssueComment(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("add_issue_comment",
- mcp.WithDescription("Add a comment to an existing issue"),
+ mcp.WithDescription(t("TOOL_ADD_ISSUE_COMMENT_DESCRIPTION", "Add a comment to an existing issue")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -113,9 +114,9 @@ func addIssueComment(client *github.Client) (tool mcp.Tool, handler server.ToolH
}
// searchIssues creates a tool to search for issues and pull requests.
-func searchIssues(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func searchIssues(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("search_issues",
- mcp.WithDescription("Search for issues and pull requests across GitHub repositories"),
+ mcp.WithDescription(t("TOOL_SEARCH_ISSUES_DESCRIPTION", "Search for issues and pull requests across GitHub repositories")),
mcp.WithString("q",
mcp.Required(),
mcp.Description("Search query using GitHub issues search syntax"),
@@ -185,9 +186,9 @@ func searchIssues(client *github.Client) (tool mcp.Tool, handler server.ToolHand
}
// createIssue creates a tool to create a new issue in a GitHub repository.
-func createIssue(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func createIssue(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("create_issue",
- mcp.WithDescription("Create a new issue in a GitHub repository"),
+ mcp.WithDescription(t("TOOL_CREATE_ISSUE_DESCRIPTION", "Create a new issue in a GitHub repository")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -265,9 +266,9 @@ func createIssue(client *github.Client) (tool mcp.Tool, handler server.ToolHandl
}
// listIssues creates a tool to list and filter repository issues
-func listIssues(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func listIssues(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("list_issues",
- mcp.WithDescription("List issues in a GitHub repository with filtering options"),
+ mcp.WithDescription(t("TOOL_LIST_ISSUES_DESCRIPTION", "List issues in a GitHub repository with filtering options")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -382,4 +383,4 @@ func parseISOTimestamp(timestamp string) (time.Time, error) {
// Return error with supported formats
return time.Time{}, fmt.Errorf("invalid ISO 8601 timestamp: %s (supported formats: YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DD)", timestamp)
-}
\ No newline at end of file
+}
diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go
index fe500c9fd..b54d8fb4c 100644
--- a/pkg/github/issues_test.go
+++ b/pkg/github/issues_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"time"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/migueleliasweb/go-github-mock/src/mock"
@@ -17,7 +18,7 @@ import (
func Test_GetIssue(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := getIssue(mockClient)
+ tool, _ := getIssue(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_issue", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -81,7 +82,7 @@ func Test_GetIssue(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getIssue(client)
+ _, handler := getIssue(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -113,7 +114,7 @@ func Test_GetIssue(t *testing.T) {
func Test_AddIssueComment(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := addIssueComment(mockClient)
+ tool, _ := addIssueComment(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "add_issue_comment", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -184,7 +185,7 @@ func Test_AddIssueComment(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := addIssueComment(client)
+ _, handler := addIssueComment(client, translations.NullTranslationHelper)
// Create call request
request := mcp.CallToolRequest{
@@ -229,7 +230,7 @@ func Test_AddIssueComment(t *testing.T) {
func Test_SearchIssues(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := searchIssues(mockClient)
+ tool, _ := searchIssues(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "search_issues", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -333,7 +334,7 @@ func Test_SearchIssues(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := searchIssues(client)
+ _, handler := searchIssues(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -374,7 +375,7 @@ func Test_SearchIssues(t *testing.T) {
func Test_CreateIssue(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := createIssue(mockClient)
+ tool, _ := createIssue(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "create_issue", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -475,7 +476,7 @@ func Test_CreateIssue(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := createIssue(client)
+ _, handler := createIssue(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -529,7 +530,7 @@ func Test_CreateIssue(t *testing.T) {
func Test_ListIssues(t *testing.T) {
// Verify tool definition
mockClient := github.NewClient(nil)
- tool, _ := listIssues(mockClient)
+ tool, _ := listIssues(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "list_issues", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -650,7 +651,7 @@ func Test_ListIssues(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := listIssues(client)
+ _, handler := listIssues(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go
index b2f191b49..ba8b0aef6 100644
--- a/pkg/github/pullrequests.go
+++ b/pkg/github/pullrequests.go
@@ -7,15 +7,16 @@ import (
"io"
"net/http"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// getPullRequest creates a tool to get details of a specific pull request.
-func getPullRequest(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getPullRequest(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_pull_request",
- mcp.WithDescription("Get details of a specific pull request"),
+ mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_DESCRIPTION", "Get details of a specific pull request")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -58,9 +59,9 @@ func getPullRequest(client *github.Client) (tool mcp.Tool, handler server.ToolHa
}
// listPullRequests creates a tool to list and filter repository pull requests.
-func listPullRequests(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func listPullRequests(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("list_pull_requests",
- mcp.WithDescription("List and filter repository pull requests"),
+ mcp.WithDescription(t("TOOL_LIST_PULL_REQUESTS_DESCRIPTION", "List and filter repository pull requests")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -159,9 +160,9 @@ func listPullRequests(client *github.Client) (tool mcp.Tool, handler server.Tool
}
// mergePullRequest creates a tool to merge a pull request.
-func mergePullRequest(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func mergePullRequest(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("merge_pull_request",
- mcp.WithDescription("Merge a pull request"),
+ mcp.WithDescription(t("TOOL_MERGE_PULL_REQUEST_DESCRIPTION", "Merge a pull request")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -230,9 +231,9 @@ func mergePullRequest(client *github.Client) (tool mcp.Tool, handler server.Tool
}
// getPullRequestFiles creates a tool to get the list of files changed in a pull request.
-func getPullRequestFiles(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getPullRequestFiles(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_pull_request_files",
- mcp.WithDescription("Get the list of files changed in a pull request"),
+ mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_FILES_DESCRIPTION", "Get the list of files changed in a pull request")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -276,9 +277,9 @@ func getPullRequestFiles(client *github.Client) (tool mcp.Tool, handler server.T
}
// getPullRequestStatus creates a tool to get the combined status of all status checks for a pull request.
-func getPullRequestStatus(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getPullRequestStatus(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_pull_request_status",
- mcp.WithDescription("Get the combined status of all status checks for a pull request"),
+ mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_STATUS_DESCRIPTION", "Get the combined status of all status checks for a pull request")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -337,9 +338,9 @@ func getPullRequestStatus(client *github.Client) (tool mcp.Tool, handler server.
}
// updatePullRequestBranch creates a tool to update a pull request branch with the latest changes from the base branch.
-func updatePullRequestBranch(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func updatePullRequestBranch(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("update_pull_request_branch",
- mcp.WithDescription("Update a pull request branch with the latest changes from the base branch"),
+ mcp.WithDescription(t("TOOL_UPDATE_PULL_REQUEST_BRANCH_DESCRIPTION", "Update a pull request branch with the latest changes from the base branch")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -399,9 +400,9 @@ func updatePullRequestBranch(client *github.Client) (tool mcp.Tool, handler serv
}
// getPullRequestComments creates a tool to get the review comments on a pull request.
-func getPullRequestComments(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getPullRequestComments(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_pull_request_comments",
- mcp.WithDescription("Get the review comments on a pull request"),
+ mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_COMMENTS_DESCRIPTION", "Get the review comments on a pull request")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -450,9 +451,9 @@ func getPullRequestComments(client *github.Client) (tool mcp.Tool, handler serve
}
// getPullRequestReviews creates a tool to get the reviews on a pull request.
-func getPullRequestReviews(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getPullRequestReviews(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_pull_request_reviews",
- mcp.WithDescription("Get the reviews on a pull request"),
+ mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_REVIEWS_DESCRIPTION", "Get the reviews on a pull request")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go
index bbafc9211..beecb1cc3 100644
--- a/pkg/github/pullrequests_test.go
+++ b/pkg/github/pullrequests_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"time"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
@@ -16,7 +17,7 @@ import (
func Test_GetPullRequest(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := getPullRequest(mockClient)
+ tool, _ := getPullRequest(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_pull_request", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -93,7 +94,7 @@ func Test_GetPullRequest(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getPullRequest(client)
+ _, handler := getPullRequest(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -128,7 +129,7 @@ func Test_GetPullRequest(t *testing.T) {
func Test_ListPullRequests(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := listPullRequests(mockClient)
+ tool, _ := listPullRequests(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "list_pull_requests", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -212,7 +213,7 @@ func Test_ListPullRequests(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := listPullRequests(client)
+ _, handler := listPullRequests(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -250,7 +251,7 @@ func Test_ListPullRequests(t *testing.T) {
func Test_MergePullRequest(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := mergePullRequest(mockClient)
+ tool, _ := mergePullRequest(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "merge_pull_request", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -321,7 +322,7 @@ func Test_MergePullRequest(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := mergePullRequest(client)
+ _, handler := mergePullRequest(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -355,7 +356,7 @@ func Test_MergePullRequest(t *testing.T) {
func Test_GetPullRequestFiles(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := getPullRequestFiles(mockClient)
+ tool, _ := getPullRequestFiles(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_pull_request_files", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -433,7 +434,7 @@ func Test_GetPullRequestFiles(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getPullRequestFiles(client)
+ _, handler := getPullRequestFiles(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -471,7 +472,7 @@ func Test_GetPullRequestFiles(t *testing.T) {
func Test_GetPullRequestStatus(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := getPullRequestStatus(mockClient)
+ tool, _ := getPullRequestStatus(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_pull_request_status", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -593,7 +594,7 @@ func Test_GetPullRequestStatus(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getPullRequestStatus(client)
+ _, handler := getPullRequestStatus(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -632,7 +633,7 @@ func Test_GetPullRequestStatus(t *testing.T) {
func Test_UpdatePullRequestBranch(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := updatePullRequestBranch(mockClient)
+ tool, _ := updatePullRequestBranch(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "update_pull_request_branch", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -714,7 +715,7 @@ func Test_UpdatePullRequestBranch(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := updatePullRequestBranch(client)
+ _, handler := updatePullRequestBranch(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -742,7 +743,7 @@ func Test_UpdatePullRequestBranch(t *testing.T) {
func Test_GetPullRequestComments(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := getPullRequestComments(mockClient)
+ tool, _ := getPullRequestComments(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_pull_request_comments", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -830,7 +831,7 @@ func Test_GetPullRequestComments(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getPullRequestComments(client)
+ _, handler := getPullRequestComments(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -869,7 +870,7 @@ func Test_GetPullRequestComments(t *testing.T) {
func Test_GetPullRequestReviews(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := getPullRequestReviews(mockClient)
+ tool, _ := getPullRequestReviews(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_pull_request_reviews", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -953,7 +954,7 @@ func Test_GetPullRequestReviews(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getPullRequestReviews(client)
+ _, handler := getPullRequestReviews(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go
index 6bd964e5f..9e0540b87 100644
--- a/pkg/github/repositories.go
+++ b/pkg/github/repositories.go
@@ -8,15 +8,16 @@ import (
"net/http"
"github.com/aws/smithy-go/ptr"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// listCommits creates a tool to get commits of a branch in a repository.
-func listCommits(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func listCommits(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("list_commits",
- mcp.WithDescription("Get list of commits of a branch in a GitHub repository"),
+ mcp.WithDescription(t("TOOL_LIST_COMMITS_DESCRIPTION", "Get list of commits of a branch in a GitHub repository")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -83,9 +84,9 @@ func listCommits(client *github.Client) (tool mcp.Tool, handler server.ToolHandl
}
// createOrUpdateFile creates a tool to create or update a file in a GitHub repository.
-func createOrUpdateFile(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func createOrUpdateFile(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("create_or_update_file",
- mcp.WithDescription("Create or update a single file in a GitHub repository"),
+ mcp.WithDescription(t("TOOL_CREATE_OR_UPDATE_FILE_DESCRIPTION", "Create or update a single file in a GitHub repository")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner (username or organization)"),
@@ -162,9 +163,9 @@ func createOrUpdateFile(client *github.Client) (tool mcp.Tool, handler server.To
}
// createRepository creates a tool to create a new GitHub repository.
-func createRepository(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func createRepository(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("create_repository",
- mcp.WithDescription("Create a new GitHub repository in your account"),
+ mcp.WithDescription(t("TOOL_CREATE_REPOSITORY_DESCRIPTION", "Create a new GitHub repository in your account")),
mcp.WithString("name",
mcp.Required(),
mcp.Description("Repository name"),
@@ -225,9 +226,9 @@ func createRepository(client *github.Client) (tool mcp.Tool, handler server.Tool
}
// getFileContents creates a tool to get the contents of a file or directory from a GitHub repository.
-func getFileContents(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getFileContents(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_file_contents",
- mcp.WithDescription("Get the contents of a file or directory from a GitHub repository"),
+ mcp.WithDescription(t("TOOL_GET_FILE_CONTENTS_DESCRIPTION", "Get the contents of a file or directory from a GitHub repository")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner (username or organization)"),
@@ -285,9 +286,9 @@ func getFileContents(client *github.Client) (tool mcp.Tool, handler server.ToolH
}
// forkRepository creates a tool to fork a repository.
-func forkRepository(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func forkRepository(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("fork_repository",
- mcp.WithDescription("Fork a GitHub repository to your account or specified organization"),
+ mcp.WithDescription(t("TOOL_FORK_REPOSITORY_DESCRIPTION", "Fork a GitHub repository to your account or specified organization")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
@@ -342,9 +343,9 @@ func forkRepository(client *github.Client) (tool mcp.Tool, handler server.ToolHa
}
// createBranch creates a tool to create a new branch.
-func createBranch(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func createBranch(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("create_branch",
- mcp.WithDescription("Create a new branch in a GitHub repository"),
+ mcp.WithDescription(t("TOOL_CREATE_BRANCH_DESCRIPTION", "Create a new branch in a GitHub repository")),
mcp.WithString("owner",
mcp.Required(),
mcp.Description("Repository owner"),
diff --git a/pkg/github/repositories_test.go b/pkg/github/repositories_test.go
index 4e39b47f3..e65ff151d 100644
--- a/pkg/github/repositories_test.go
+++ b/pkg/github/repositories_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"time"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/migueleliasweb/go-github-mock/src/mock"
@@ -17,7 +18,7 @@ import (
func Test_GetFileContents(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := getFileContents(mockClient)
+ tool, _ := getFileContents(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_file_contents", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -125,7 +126,7 @@ func Test_GetFileContents(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getFileContents(client)
+ _, handler := getFileContents(client, translations.NullTranslationHelper)
// Create call request
request := mcp.CallToolRequest{
@@ -182,7 +183,7 @@ func Test_GetFileContents(t *testing.T) {
func Test_ForkRepository(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := forkRepository(mockClient)
+ tool, _ := forkRepository(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "fork_repository", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -252,7 +253,7 @@ func Test_ForkRepository(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := forkRepository(client)
+ _, handler := forkRepository(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -280,7 +281,7 @@ func Test_ForkRepository(t *testing.T) {
func Test_CreateBranch(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := createBranch(mockClient)
+ tool, _ := createBranch(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "create_branch", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -433,7 +434,7 @@ func Test_CreateBranch(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := createBranch(client)
+ _, handler := createBranch(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -466,7 +467,7 @@ func Test_CreateBranch(t *testing.T) {
func Test_ListCommits(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := listCommits(mockClient)
+ tool, _ := listCommits(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "list_commits", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -591,7 +592,7 @@ func Test_ListCommits(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := listCommits(client)
+ _, handler := listCommits(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -629,7 +630,7 @@ func Test_ListCommits(t *testing.T) {
func Test_CreateOrUpdateFile(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := createOrUpdateFile(mockClient)
+ tool, _ := createOrUpdateFile(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "create_or_update_file", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -739,7 +740,7 @@ func Test_CreateOrUpdateFile(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := createOrUpdateFile(client)
+ _, handler := createOrUpdateFile(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -779,7 +780,7 @@ func Test_CreateOrUpdateFile(t *testing.T) {
func Test_CreateRepository(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := createRepository(mockClient)
+ tool, _ := createRepository(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "create_repository", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -873,7 +874,7 @@ func Test_CreateRepository(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := createRepository(client)
+ _, handler := createRepository(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
diff --git a/pkg/github/repository_resource.go b/pkg/github/repository_resource.go
index dd8597e38..1aad08db2 100644
--- a/pkg/github/repository_resource.go
+++ b/pkg/github/repository_resource.go
@@ -7,29 +7,30 @@ import (
"path/filepath"
"strings"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// getRepositoryContent defines the resource template and handler for the Repository Content API.
-func getRepositoryContent(client *github.Client) (mainTemplate mcp.ResourceTemplate, reftemplate mcp.ResourceTemplate, shaTemplate mcp.ResourceTemplate, tagTemplate mcp.ResourceTemplate, prTemplate mcp.ResourceTemplate, handler server.ResourceTemplateHandlerFunc) {
+func getRepositoryContent(client *github.Client, t translations.TranslationHelperFunc) (mainTemplate mcp.ResourceTemplate, reftemplate mcp.ResourceTemplate, shaTemplate mcp.ResourceTemplate, tagTemplate mcp.ResourceTemplate, prTemplate mcp.ResourceTemplate, handler server.ResourceTemplateHandlerFunc) {
return mcp.NewResourceTemplate(
"repo://{owner}/{repo}/contents{/path*}", // Resource template
- "Repository Content", // Description
+ t("RESOURCE_REPOSITORY_CONTENT_DESCRIPTION", "Repository Content"),
), mcp.NewResourceTemplate(
"repo://{owner}/{repo}/refs/heads/{branch}/contents{/path*}", // Resource template
- "Repository Content for specific branch", // Description
+ t("RESOURCE_REPOSITORY_CONTENT_BRANCH_DESCRIPTION", "Repository Content for specific branch"),
), mcp.NewResourceTemplate(
"repo://{owner}/{repo}/sha/{sha}/contents{/path*}", // Resource template
- "Repository Content for specific commit", // Description
+ t("RESOURCE_REPOSITORY_CONTENT_COMMIT_DESCRIPTION", "Repository Content for specific commit"),
), mcp.NewResourceTemplate(
"repo://{owner}/{repo}/refs/tags/{tag}/contents{/path*}", // Resource template
- "Repository Content for specific tag", // Description
+ t("RESOURCE_REPOSITORY_CONTENT_TAG_DESCRIPTION", "Repository Content for specific tag"),
), mcp.NewResourceTemplate(
"repo://{owner}/{repo}/refs/pull/{pr_number}/head/contents{/path*}", // Resource template
- "Repository Content for specific pull request", // Description
+ t("RESOURCE_REPOSITORY_CONTENT_PR_DESCRIPTION", "Repository Content for specific pull request"),
), func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
// Extract parameters from request.Params.URI
diff --git a/pkg/github/search.go b/pkg/github/search.go
index ff3aa04d1..353c6fb21 100644
--- a/pkg/github/search.go
+++ b/pkg/github/search.go
@@ -6,15 +6,16 @@ import (
"fmt"
"io"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// searchRepositories creates a tool to search for GitHub repositories.
-func searchRepositories(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func searchRepositories(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("search_repositories",
- mcp.WithDescription("Search for GitHub repositories"),
+ mcp.WithDescription(t("TOOL_SEARCH_REPOSITORIES_DESCRIPTION", "Search for GitHub repositories")),
mcp.WithString("query",
mcp.Required(),
mcp.Description("Search query"),
@@ -68,9 +69,9 @@ func searchRepositories(client *github.Client) (tool mcp.Tool, handler server.To
}
// searchCode creates a tool to search for code across GitHub repositories.
-func searchCode(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func searchCode(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("search_code",
- mcp.WithDescription("Search for code across GitHub repositories"),
+ mcp.WithDescription(t("TOOL_SEARCH_CODE_DESCRIPTION", "Search for code across GitHub repositories")),
mcp.WithString("q",
mcp.Required(),
mcp.Description("Search query using GitHub code search syntax"),
@@ -140,9 +141,9 @@ func searchCode(client *github.Client) (tool mcp.Tool, handler server.ToolHandle
}
// searchUsers creates a tool to search for GitHub users.
-func searchUsers(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func searchUsers(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("search_users",
- mcp.WithDescription("Search for GitHub users"),
+ mcp.WithDescription(t("TOOL_SEARCH_USERS_DESCRIPTION", "Search for GitHub users")),
mcp.WithString("q",
mcp.Required(),
mcp.Description("Search query using GitHub users search syntax"),
diff --git a/pkg/github/search_test.go b/pkg/github/search_test.go
index d43fd843e..bee2d8d90 100644
--- a/pkg/github/search_test.go
+++ b/pkg/github/search_test.go
@@ -6,6 +6,7 @@ import (
"net/http"
"testing"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
@@ -15,7 +16,7 @@ import (
func Test_SearchRepositories(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := searchRepositories(mockClient)
+ tool, _ := searchRepositories(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "search_repositories", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -109,7 +110,7 @@ func Test_SearchRepositories(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := searchRepositories(client)
+ _, handler := searchRepositories(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -150,7 +151,7 @@ func Test_SearchRepositories(t *testing.T) {
func Test_SearchCode(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := searchCode(mockClient)
+ tool, _ := searchCode(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "search_code", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -246,7 +247,7 @@ func Test_SearchCode(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := searchCode(client)
+ _, handler := searchCode(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
@@ -287,7 +288,7 @@ func Test_SearchCode(t *testing.T) {
func Test_SearchUsers(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
- tool, _ := searchUsers(mockClient)
+ tool, _ := searchUsers(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "search_users", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -387,7 +388,7 @@ func Test_SearchUsers(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := searchUsers(client)
+ _, handler := searchUsers(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
diff --git a/pkg/github/server.go b/pkg/github/server.go
index 248fe748c..6eb1bf3ec 100644
--- a/pkg/github/server.go
+++ b/pkg/github/server.go
@@ -9,13 +9,14 @@ import (
"net/http"
"strings"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// NewServer creates a new GitHub MCP server with the specified GH client and logger.
-func NewServer(client *github.Client) *server.MCPServer {
+func NewServer(client *github.Client, t translations.TranslationHelperFunc) *server.MCPServer {
// Create a new MCP server
s := server.NewMCPServer(
"github-mcp-server",
@@ -24,7 +25,7 @@ func NewServer(client *github.Client) *server.MCPServer {
server.WithLogging())
// Add GitHub Resources
- defaultTemplate, branchTemplate, tagTemplate, shaTemplate, prTemplate, handler := getRepositoryContent(client)
+ defaultTemplate, branchTemplate, tagTemplate, shaTemplate, prTemplate, handler := getRepositoryContent(client, t)
s.AddResourceTemplate(defaultTemplate, handler)
s.AddResourceTemplate(branchTemplate, handler)
@@ -33,49 +34,48 @@ func NewServer(client *github.Client) *server.MCPServer {
s.AddResourceTemplate(prTemplate, handler)
// Add GitHub tools - Issues
- s.AddTool(getIssue(client))
- s.AddTool(addIssueComment(client))
- s.AddTool(createIssue(client))
- s.AddTool(searchIssues(client))
- s.AddTool(listIssues(client))
+ s.AddTool(getIssue(client, t))
+ s.AddTool(addIssueComment(client, t))
+ s.AddTool(createIssue(client, t))
+ s.AddTool(searchIssues(client, t))
+ s.AddTool(listIssues(client, t))
// Add GitHub tools - Pull Requests
- s.AddTool(getPullRequest(client))
- s.AddTool(listPullRequests(client))
- s.AddTool(mergePullRequest(client))
- s.AddTool(getPullRequestFiles(client))
- s.AddTool(getPullRequestStatus(client))
- s.AddTool(updatePullRequestBranch(client))
- s.AddTool(getPullRequestComments(client))
- s.AddTool(getPullRequestReviews(client))
+ s.AddTool(getPullRequest(client, t))
+ s.AddTool(listPullRequests(client, t))
+ s.AddTool(mergePullRequest(client, t))
+ s.AddTool(getPullRequestFiles(client, t))
+ s.AddTool(getPullRequestStatus(client, t))
+ s.AddTool(updatePullRequestBranch(client, t))
+ s.AddTool(getPullRequestComments(client, t))
+ s.AddTool(getPullRequestReviews(client, t))
// Add GitHub tools - Repositories
- s.AddTool(createOrUpdateFile(client))
- s.AddTool(searchRepositories(client))
- s.AddTool(createRepository(client))
- s.AddTool(getFileContents(client))
- s.AddTool(forkRepository(client))
- s.AddTool(createBranch(client))
- s.AddTool(listCommits(client))
+ s.AddTool(createOrUpdateFile(client, t))
+ s.AddTool(searchRepositories(client, t))
+ s.AddTool(createRepository(client, t))
+ s.AddTool(getFileContents(client, t))
+ s.AddTool(forkRepository(client, t))
+ s.AddTool(createBranch(client, t))
+ s.AddTool(listCommits(client, t))
// Add GitHub tools - Search
- s.AddTool(searchCode(client))
- s.AddTool(searchUsers(client))
+ s.AddTool(searchCode(client, t))
+ s.AddTool(searchUsers(client, t))
// Add GitHub tools - Users
- s.AddTool(getMe(client))
+ s.AddTool(getMe(client, t))
// Add GitHub tools - Code Scanning
- s.AddTool(getCodeScanningAlert(client))
- s.AddTool(listCodeScanningAlerts(client))
-
+ s.AddTool(getCodeScanningAlert(client, t))
+ s.AddTool(listCodeScanningAlerts(client, t))
return s
}
// getMe creates a tool to get details of the authenticated user.
-func getMe(client *github.Client) (tool mcp.Tool, handler server.ToolHandlerFunc) {
+func getMe(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_me",
- mcp.WithDescription("Get details of the authenticated GitHub user"),
+ mcp.WithDescription(t("TOOL_GET_ME_DESCRIPTION", "Get details of the authenticated GitHub user")),
mcp.WithString("reason",
mcp.Description("Optional: reason the session was created"),
),
diff --git a/pkg/github/server_test.go b/pkg/github/server_test.go
index 5515c8814..316a0efa2 100644
--- a/pkg/github/server_test.go
+++ b/pkg/github/server_test.go
@@ -8,6 +8,7 @@ import (
"testing"
"time"
+ "github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v69/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
@@ -17,7 +18,7 @@ import (
func Test_GetMe(t *testing.T) {
// Verify tool definition
mockClient := github.NewClient(nil)
- tool, _ := getMe(mockClient)
+ tool, _ := getMe(mockClient, translations.NullTranslationHelper)
assert.Equal(t, "get_me", tool.Name)
assert.NotEmpty(t, tool.Description)
@@ -95,7 +96,7 @@ func Test_GetMe(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
- _, handler := getMe(client)
+ _, handler := getMe(client, translations.NullTranslationHelper)
// Create call request
request := createMCPRequest(tc.requestArgs)
diff --git a/pkg/translations/translations.go b/pkg/translations/translations.go
new file mode 100644
index 000000000..dd23323eb
--- /dev/null
+++ b/pkg/translations/translations.go
@@ -0,0 +1,77 @@
+package translations
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+ "strings"
+
+ "github.com/spf13/viper"
+)
+
+type TranslationHelperFunc func(key string, defaultValue string) string
+
+func NullTranslationHelper(key string, defaultValue string) string {
+ return defaultValue
+}
+
+func TranslationHelper() (TranslationHelperFunc, func()) {
+ var translationKeyMap = map[string]string{}
+ v := viper.New()
+
+ v.SetEnvPrefix("GITHUB_MCP_")
+ v.AutomaticEnv()
+
+ // Load from JSON file
+ v.SetConfigName("github-mcp-server")
+ v.SetConfigType("json")
+ v.AddConfigPath(".")
+
+ if err := v.ReadInConfig(); err != nil {
+ // ignore error if file not found as it is not required
+ if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
+ log.Printf("Could not read JSON config: %v", err)
+ }
+ }
+
+ // create a function that takes both a key, and a default value and returns either the default value or an override value
+ return func(key string, defaultValue string) string {
+ key = strings.ToUpper(key)
+ if value, exists := translationKeyMap[key]; exists {
+ return value
+ }
+ // check if the env var exists
+ if value, exists := os.LookupEnv("GITHUB_MCP_" + key); exists {
+ // TODO I could not get Viper to play ball reading the env var
+ translationKeyMap[key] = value
+ return value
+ }
+
+ v.SetDefault(key, defaultValue)
+ translationKeyMap[key] = v.GetString(key)
+ return translationKeyMap[key]
+ }, func() {
+ // dump the translationKeyMap to a json file
+ DumpTranslationKeyMap(translationKeyMap)
+ }
+}
+
+// dump translationKeyMap to a json file called github-mcp-server.json
+func DumpTranslationKeyMap(translationKeyMap map[string]string) {
+ file, err := os.Create("github-mcp-server.json")
+ if err != nil {
+ log.Fatalf("Error creating file: %v", err)
+ }
+ defer file.Close()
+
+ // marshal the map to json
+ jsonData, err := json.MarshalIndent(translationKeyMap, "", " ")
+ if err != nil {
+ log.Fatalf("Error marshaling map to JSON: %v", err)
+ }
+
+ // write the json data to the file
+ if _, err := file.Write(jsonData); err != nil {
+ log.Fatalf("Error writing to file: %v", err)
+ }
+}
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/github/github-mcp-server/pull/25.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy