add a framework for translations
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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"),
|
||||
|
||||
@@ -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
@@ -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"),
|
||||
|
||||
@@ -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,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
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
@@ -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"),
|
||||
),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user