add a framework for translations

This commit is contained in:
Sam Morrow
2025-03-19 19:04:27 +01:00
committed by Sam Morrow
parent b3b3e02afd
commit dd46f8a837
16 changed files with 267 additions and 138 deletions
+30
View File
@@ -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:
+13 -3
View File
@@ -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() {
+5 -4
View File
@@ -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."),
+5 -4
View File
@@ -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)
+12 -11
View File
@@ -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)
}
}
+11 -10
View File
@@ -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)
+17 -16
View File
@@ -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"),
+17 -16
View File
@@ -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)
+13 -12
View File
@@ -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"),
+13 -12
View File
@@ -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)
+7 -6
View File
@@ -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
+7 -6
View File
@@ -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"),
+7 -6
View File
@@ -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)
+30 -30
View File
@@ -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"),
),
+3 -2
View File
@@ -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)
+77
View File
@@ -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)
}
}