diff --git a/README.md b/README.md index 1b926b132..1c4b6484f 100644 --- a/README.md +++ b/README.md @@ -1498,6 +1498,30 @@ Following tools will filter out content from users lacking the push access: - `pull_request_read:get_review_comments` - `pull_request_read:get_reviews` +## Running multiple servers simultaneously +When running multiple GitHub MCP servers simultaniously you should consider to set the server's name and title to differentiate between them. + +```bash +./github-mcp-server --server-name --server-title +``` + +When running with Docker, set the corresponding environment variable: + +```bash +docker run -i --rm \ + -e GITHUB_PERSONAL_ACCESS_TOKEN= \ + -e GITHUB_SERVER_NAME= \ + -e GITHUB_SERVER_TITLE= + ghcr.io/github/github-mcp-server +``` + +### Example: +To connect to your GitHub Enterprise Server as well as to `github.com` one can start two servers locally like this: +```bash +./github-mcp-server --server-name "github-mcp-server-dotcom" --server-title "GitHub MCP Server for github.com" +./github-mcp-server --server-name "github-mcp-server-ghes" --server-title "GitHub MCP Server for my GHES" +``` + ## i18n / Overriding Descriptions The descriptions of the tools can be overridden by creating a diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go index 05c2c6e0b..ac98bcd9d 100644 --- a/cmd/github-mcp-server/main.go +++ b/cmd/github-mcp-server/main.go @@ -108,6 +108,8 @@ var ( ttl := viper.GetDuration("repo-access-cache-ttl") httpConfig := ghhttp.ServerConfig{ Version: version, + Name: viper.GetString("server-name"), + Title: viper.GetString("server-title"), Host: viper.GetString("host"), Port: viper.GetInt("port"), BaseURL: viper.GetString("base-url"), @@ -139,6 +141,8 @@ func init() { rootCmd.PersistentFlags().StringSlice("features", nil, "Comma-separated list of feature flags to enable") rootCmd.PersistentFlags().Bool("dynamic-toolsets", false, "Enable dynamic toolsets") rootCmd.PersistentFlags().Bool("read-only", false, "Restrict the server to read-only operations") + rootCmd.PersistentFlags().String("server-name", "github-mcp-server", "Name provided by the server during initialization") + rootCmd.PersistentFlags().String("server-title", "GitHub MCP Server", "Title provided the server during initialization") 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") @@ -161,6 +165,8 @@ func init() { _ = viper.BindPFlag("features", rootCmd.PersistentFlags().Lookup("features")) _ = viper.BindPFlag("dynamic_toolsets", rootCmd.PersistentFlags().Lookup("dynamic-toolsets")) _ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only")) + _ = viper.BindPFlag("server-name", rootCmd.PersistentFlags().Lookup("server-name")) + _ = viper.BindPFlag("server-title", rootCmd.PersistentFlags().Lookup("server-title")) _ = 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")) diff --git a/pkg/github/server.go b/pkg/github/server.go index 06c12575d..1c0644fe9 100644 --- a/pkg/github/server.go +++ b/pkg/github/server.go @@ -20,6 +20,12 @@ type MCPServerConfig struct { // Version of the server Version string + // Name of the server used during initialization + Name string + + // Title of the server used during initialization + Title string + // GitHub Host to target for API requests (e.g. github.com or github.enterprise.com) Host string @@ -101,7 +107,14 @@ func NewMCPServer(ctx context.Context, cfg *MCPServerConfig, deps ToolDependenci } } - ghServer := NewServer(cfg.Version, serverOpts) + serverInfo := &mcp.Implementation{ + Name: cfg.Name, + Title: cfg.Title, + Version: cfg.Version, + Icons: octicons.Icons("mark-github"), + } + + ghServer := NewServer(serverInfo, serverOpts) // Add middlewares. Order matters - for example, the error context middleware should be applied last so that it runs FIRST (closest to the handler) to ensure all errors are captured, // and any middleware that needs to read or modify the context should be before it. @@ -177,20 +190,13 @@ func addGitHubAPIErrorToContext(next mcp.MethodHandler) mcp.MethodHandler { } // NewServer creates a new GitHub MCP server with the specified GH client and logger. -func NewServer(version string, opts *mcp.ServerOptions) *mcp.Server { +func NewServer(serverInfo *mcp.Implementation, opts *mcp.ServerOptions) *mcp.Server { if opts == nil { opts = &mcp.ServerOptions{} } // Create a new MCP server - s := mcp.NewServer(&mcp.Implementation{ - Name: "github-mcp-server", - Title: "GitHub MCP Server", - Version: version, - Icons: octicons.Icons("mark-github"), - }, opts) - - return s + return mcp.NewServer(serverInfo, opts) } func CompletionsHandler(getClient GetClientFn) func(ctx context.Context, req *mcp.CompleteRequest) (*mcp.CompleteResult, error) { diff --git a/pkg/http/handler.go b/pkg/http/handler.go index 2e828211d..e8506f75b 100644 --- a/pkg/http/handler.go +++ b/pkg/http/handler.go @@ -201,6 +201,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ghServer, err := h.githubMcpServerFactory(r, h.deps, invToUse, &github.MCPServerConfig{ Version: h.config.Version, + Name: h.config.Name, + Title: h.config.Title, Translator: h.t, ContentWindowSize: h.config.ContentWindowSize, Logger: h.logger, diff --git a/pkg/http/server.go b/pkg/http/server.go index 872303940..af6c210fd 100644 --- a/pkg/http/server.go +++ b/pkg/http/server.go @@ -31,6 +31,12 @@ type ServerConfig struct { // Version of the server Version string + // Name of the server + Name string + + // Title of the server + Title string + // GitHub Host to target for API requests (e.g. github.com or github.enterprise.com) Host string