From 29977e32252bc676b485a3fa9ba0cf79ff054167 Mon Sep 17 00:00:00 2001 From: hungran <26101787+hungran@users.noreply.github.com> Date: Thu, 10 Jul 2025 19:20:01 +0700 Subject: [PATCH 1/4] feat: tool prefix name Signed-off-by: hungran <26101787+hungran@users.noreply.github.com> --- cmd/github-mcp-server/main.go | 3 +++ internal/ghmcp/server.go | 11 +++++++++-- pkg/toolsets/toolsets.go | 36 ++++++++++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go index cad002666..1605f4523 100644 --- a/cmd/github-mcp-server/main.go +++ b/cmd/github-mcp-server/main.go @@ -52,6 +52,7 @@ var ( EnabledToolsets: enabledToolsets, DynamicToolsets: viper.GetBool("dynamic_toolsets"), ReadOnly: viper.GetBool("read-only"), + ToolPrefix: viper.GetString("tool-prefix"), ExportTranslations: viper.GetBool("export-translations"), EnableCommandLogging: viper.GetBool("enable-command-logging"), LogFilePath: viper.GetString("log-file"), @@ -71,6 +72,7 @@ func init() { rootCmd.PersistentFlags().StringSlice("toolsets", github.DefaultTools, "An optional comma separated list of groups of tools to allow, defaults to enabling all") 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("tool-prefix", "", "Optional prefix to add to all tool names (e.g. 'github_')") 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") @@ -80,6 +82,7 @@ func init() { _ = viper.BindPFlag("toolsets", rootCmd.PersistentFlags().Lookup("toolsets")) _ = viper.BindPFlag("dynamic_toolsets", rootCmd.PersistentFlags().Lookup("dynamic-toolsets")) _ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only")) + _ = viper.BindPFlag("tool-prefix", rootCmd.PersistentFlags().Lookup("tool-prefix")) _ = 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/internal/ghmcp/server.go b/internal/ghmcp/server.go index 568af10d1..9171a0c76 100644 --- a/internal/ghmcp/server.go +++ b/internal/ghmcp/server.go @@ -45,6 +45,9 @@ type MCPServerConfig struct { // ReadOnly indicates if we should only offer read-only tools ReadOnly bool + // ToolPrefix is an optional prefix to add to all tool names (e.g. "github_") + ToolPrefix string + // Translator provides translated text for the server tooling Translator translations.TranslationHelperFunc } @@ -138,11 +141,11 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) { } // Register all mcp functionality with the server - tsg.RegisterAll(ghServer) + tsg.RegisterAllWithPrefix(ghServer, cfg.ToolPrefix) if cfg.DynamicToolsets { dynamic := github.InitDynamicToolset(ghServer, tsg, cfg.Translator) - dynamic.RegisterTools(ghServer) + dynamic.RegisterToolsWithPrefix(ghServer, cfg.ToolPrefix) } return ghServer, nil @@ -169,6 +172,9 @@ type StdioServerConfig struct { // ReadOnly indicates if we should only register read-only tools ReadOnly bool + // ToolPrefix is an optional prefix to add to all tool names (e.g. "github_") + ToolPrefix string + // ExportTranslations indicates if we should export translations // See: https://github.com/github/github-mcp-server?tab=readme-ov-file#i18n--overriding-descriptions ExportTranslations bool @@ -195,6 +201,7 @@ func RunStdioServer(cfg StdioServerConfig) error { EnabledToolsets: cfg.EnabledToolsets, DynamicToolsets: cfg.DynamicToolsets, ReadOnly: cfg.ReadOnly, + ToolPrefix: cfg.ToolPrefix, Translator: t, }) if err != nil { diff --git a/pkg/toolsets/toolsets.go b/pkg/toolsets/toolsets.go index 5d503b742..4fc58070c 100644 --- a/pkg/toolsets/toolsets.go +++ b/pkg/toolsets/toolsets.go @@ -29,6 +29,18 @@ func NewToolsetDoesNotExistError(name string) *ToolsetDoesNotExistError { return &ToolsetDoesNotExistError{Name: name} } +// createToolWithPrefixedName creates a new tool with the same properties as the original but with a prefixed name +func createToolWithPrefixedName(original mcp.Tool, prefix string) mcp.Tool { + // Create a new tool with the prefixed name and copy all properties + newTool := mcp.Tool{ + Name: prefix + original.Name, + Description: original.Description, + InputSchema: original.InputSchema, + Annotations: original.Annotations, + } + return newTool +} + func NewServerTool(tool mcp.Tool, handler server.ToolHandlerFunc) server.ServerTool { return server.ServerTool{Tool: tool, Handler: handler} } @@ -92,15 +104,29 @@ func (t *Toolset) GetAvailableTools() []server.ServerTool { } func (t *Toolset) RegisterTools(s *server.MCPServer) { + t.RegisterToolsWithPrefix(s, "") +} + +func (t *Toolset) RegisterToolsWithPrefix(s *server.MCPServer, prefix string) { if !t.Enabled { return } for _, tool := range t.readTools { - s.AddTool(tool.Tool, tool.Handler) + toolToRegister := tool.Tool + if prefix != "" { + // Create a new tool with the prefixed name + toolToRegister = createToolWithPrefixedName(tool.Tool, prefix) + } + s.AddTool(toolToRegister, tool.Handler) } if !t.readOnly { for _, tool := range t.writeTools { - s.AddTool(tool.Tool, tool.Handler) + toolToRegister := tool.Tool + if prefix != "" { + // Create a new tool with the prefixed name + toolToRegister = createToolWithPrefixedName(tool.Tool, prefix) + } + s.AddTool(toolToRegister, tool.Handler) } } } @@ -251,8 +277,12 @@ func (tg *ToolsetGroup) EnableToolset(name string) error { } func (tg *ToolsetGroup) RegisterAll(s *server.MCPServer) { + tg.RegisterAllWithPrefix(s, "") +} + +func (tg *ToolsetGroup) RegisterAllWithPrefix(s *server.MCPServer, prefix string) { for _, toolset := range tg.Toolsets { - toolset.RegisterTools(s) + toolset.RegisterToolsWithPrefix(s, prefix) toolset.RegisterResourcesTemplates(s) toolset.RegisterPrompts(s) } From 20d248cd6260deb434fee6013cd0b25bb6e7f370 Mon Sep 17 00:00:00 2001 From: hungran <26101787+hungran@users.noreply.github.com> Date: Fri, 11 Jul 2025 13:02:24 +0700 Subject: [PATCH 2/4] keep scope minial Signed-off-by: hungran <26101787+hungran@users.noreply.github.com> --- internal/ghmcp/server.go | 4 ++-- pkg/toolsets/toolsets.go | 14 +++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/internal/ghmcp/server.go b/internal/ghmcp/server.go index 9171a0c76..3c307b886 100644 --- a/internal/ghmcp/server.go +++ b/internal/ghmcp/server.go @@ -141,11 +141,11 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) { } // Register all mcp functionality with the server - tsg.RegisterAllWithPrefix(ghServer, cfg.ToolPrefix) + tsg.RegisterAll(ghServer, cfg.ToolPrefix) if cfg.DynamicToolsets { dynamic := github.InitDynamicToolset(ghServer, tsg, cfg.Translator) - dynamic.RegisterToolsWithPrefix(ghServer, cfg.ToolPrefix) + dynamic.RegisterTools(ghServer, cfg.ToolPrefix) } return ghServer, nil diff --git a/pkg/toolsets/toolsets.go b/pkg/toolsets/toolsets.go index 4fc58070c..212ff8484 100644 --- a/pkg/toolsets/toolsets.go +++ b/pkg/toolsets/toolsets.go @@ -103,11 +103,7 @@ func (t *Toolset) GetAvailableTools() []server.ServerTool { return append(t.readTools, t.writeTools...) } -func (t *Toolset) RegisterTools(s *server.MCPServer) { - t.RegisterToolsWithPrefix(s, "") -} - -func (t *Toolset) RegisterToolsWithPrefix(s *server.MCPServer, prefix string) { +func (t *Toolset) RegisterTools(s *server.MCPServer, prefix string) { if !t.Enabled { return } @@ -276,13 +272,9 @@ func (tg *ToolsetGroup) EnableToolset(name string) error { return nil } -func (tg *ToolsetGroup) RegisterAll(s *server.MCPServer) { - tg.RegisterAllWithPrefix(s, "") -} - -func (tg *ToolsetGroup) RegisterAllWithPrefix(s *server.MCPServer, prefix string) { +func (tg *ToolsetGroup) RegisterAll(s *server.MCPServer, prefix string) { for _, toolset := range tg.Toolsets { - toolset.RegisterToolsWithPrefix(s, prefix) + toolset.RegisterTools(s, prefix) toolset.RegisterResourcesTemplates(s) toolset.RegisterPrompts(s) } From 8a38cd8ff460ad16a12fbce82b8e5bdeba88242f Mon Sep 17 00:00:00 2001 From: hungran <26101787+hungran@users.noreply.github.com> Date: Fri, 11 Jul 2025 13:10:21 +0700 Subject: [PATCH 3/4] reduce duplicate read write tool loops Signed-off-by: hungran <26101787+hungran@users.noreply.github.com> --- pkg/toolsets/toolsets.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/toolsets/toolsets.go b/pkg/toolsets/toolsets.go index 212ff8484..1d0fc695e 100644 --- a/pkg/toolsets/toolsets.go +++ b/pkg/toolsets/toolsets.go @@ -107,7 +107,7 @@ func (t *Toolset) RegisterTools(s *server.MCPServer, prefix string) { if !t.Enabled { return } - for _, tool := range t.readTools { + registerToolWithPrefix := func(tool server.ServerTool) { toolToRegister := tool.Tool if prefix != "" { // Create a new tool with the prefixed name @@ -115,14 +115,13 @@ func (t *Toolset) RegisterTools(s *server.MCPServer, prefix string) { } s.AddTool(toolToRegister, tool.Handler) } + + for _, tool := range t.readTools { + registerToolWithPrefix(tool) + } if !t.readOnly { for _, tool := range t.writeTools { - toolToRegister := tool.Tool - if prefix != "" { - // Create a new tool with the prefixed name - toolToRegister = createToolWithPrefixedName(tool.Tool, prefix) - } - s.AddTool(toolToRegister, tool.Handler) + registerToolWithPrefix(tool) } } } From ca62561c466a5be84385af7d114997660932eb0d Mon Sep 17 00:00:00 2001 From: hungran <26101787+hungran@users.noreply.github.com> Date: Fri, 11 Jul 2025 13:30:52 +0700 Subject: [PATCH 4/4] simpler Signed-off-by: hungran <26101787+hungran@users.noreply.github.com> --- pkg/toolsets/toolsets.go | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/pkg/toolsets/toolsets.go b/pkg/toolsets/toolsets.go index 1d0fc695e..81dad9cab 100644 --- a/pkg/toolsets/toolsets.go +++ b/pkg/toolsets/toolsets.go @@ -29,18 +29,6 @@ func NewToolsetDoesNotExistError(name string) *ToolsetDoesNotExistError { return &ToolsetDoesNotExistError{Name: name} } -// createToolWithPrefixedName creates a new tool with the same properties as the original but with a prefixed name -func createToolWithPrefixedName(original mcp.Tool, prefix string) mcp.Tool { - // Create a new tool with the prefixed name and copy all properties - newTool := mcp.Tool{ - Name: prefix + original.Name, - Description: original.Description, - InputSchema: original.InputSchema, - Annotations: original.Annotations, - } - return newTool -} - func NewServerTool(tool mcp.Tool, handler server.ToolHandlerFunc) server.ServerTool { return server.ServerTool{Tool: tool, Handler: handler} } @@ -108,12 +96,11 @@ func (t *Toolset) RegisterTools(s *server.MCPServer, prefix string) { return } registerToolWithPrefix := func(tool server.ServerTool) { - toolToRegister := tool.Tool if prefix != "" { // Create a new tool with the prefixed name - toolToRegister = createToolWithPrefixedName(tool.Tool, prefix) + tool.Tool.Name = prefix + tool.Tool.Name } - s.AddTool(toolToRegister, tool.Handler) + s.AddTool(tool.Tool, tool.Handler) } for _, tool := range t.readTools {