Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Tommy/tool-specific-config-support#1394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
tommaso-moro wants to merge5 commits intomain
base:main
Choose a base branch
Loading
fromtommy/tool-specific-config-support
Draft
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletionsREADME.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -343,6 +343,28 @@ To specify toolsets you want available to the LLM, you can pass an allow-list in

The environment variable `GITHUB_TOOLSETS` takes precedence over the command line argument if both are provided.

#### Specifying Individual Tools

You can also configure specific tools instead of entire toolsets using the `--tools` flag. When tools are specified, they take priority over toolsets configuration, read-only mode, and dynamic toolsets.

1. **Using Command Line Argument**:

```bash
github-mcp-server --tools get_file_contents,issue_read,create_pull_request
```

2. **Using Environment Variable**:
```bash
GITHUB_TOOLS="get_file_contents,issue_read,create_pull_request" ./github-mcp-server
```

**Important Notes:**
- When `--tools` is specified, only the listed tools are registered, bypassing toolset enablement
- Read-only mode is still respected: write tools are skipped if `--read-only` is set, even if explicitly requested
- Dynamic toolsets are disabled when specific tools are configured
- Resources and prompts from all toolsets are still registered to maintain functionality
- Tool names must match exactly (e.g., `get_file_contents`, not `getFileContents`)

### Using Toolsets With Docker

When using Docker, you can pass the toolsets as environment variables:
Expand All@@ -354,6 +376,17 @@ docker run -i --rm \
ghcr.io/github/github-mcp-server
```

### Using Tools With Docker

When using Docker, you can pass specific tools as environment variables:

```bash
docker run -i --rm \
-e GITHUB_PERSONAL_ACCESS_TOKEN=<your-token> \
-e GITHUB_TOOLS="get_file_contents,issue_read,create_pull_request" \
ghcr.io/github/github-mcp-server
```

### Special toolsets

#### "all" toolset
Expand Down
9 changes: 9 additions & 0 deletionscmd/github-mcp-server/main.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,11 +50,18 @@ var (
enabledToolsets = []string{github.ToolsetMetadataDefault.ID}
}

// Parse tools (similar to toolsets)
var enabledTools []string
if err := viper.UnmarshalKey("tools", &enabledTools); err != nil {
return fmt.Errorf("failed to unmarshal tools: %w", err)
}

stdioServerConfig := ghmcp.StdioServerConfig{
Version: version,
Host: viper.GetString("host"),
Token: token,
EnabledToolsets: enabledToolsets,
EnabledTools: enabledTools,
DynamicToolsets: viper.GetBool("dynamic_toolsets"),
ReadOnly: viper.GetBool("read-only"),
ExportTranslations: viper.GetBool("export-translations"),
Expand All@@ -76,6 +83,7 @@ func init() {

// Add global flags that will be shared by all commands
rootCmd.PersistentFlags().StringSlice("toolsets", nil, github.GenerateToolsetsHelp())
rootCmd.PersistentFlags().StringSlice("tools", nil, "Comma-separated list of specific tools to enable (takes priority over toolsets)")
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("log-file", "", "Path to log file")
Expand All@@ -87,6 +95,7 @@ func init() {

// Bind flag to viper
_ = viper.BindPFlag("toolsets", rootCmd.PersistentFlags().Lookup("toolsets"))
_ = viper.BindPFlag("tools", rootCmd.PersistentFlags().Lookup("tools"))
_ = viper.BindPFlag("dynamic_toolsets", rootCmd.PersistentFlags().Lookup("dynamic-toolsets"))
_ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only"))
_ = viper.BindPFlag("log-file", rootCmd.PersistentFlags().Lookup("log-file"))
Expand Down
51 changes: 42 additions & 9 deletionsinternal/ghmcp/server.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -39,6 +39,10 @@ type MCPServerConfig struct {
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration
EnabledToolsets []string

// EnabledTools is a list of specific tools to enable (takes priority over toolsets)
// When specified, only these tools will be registered, bypassing toolset enablement
EnabledTools []string

// Whether to enable dynamic toolsets
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#dynamic-tool-discovery
DynamicToolsetsbool
Expand DownExpand Up@@ -166,18 +170,42 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
cfg.ContentWindowSize,
github.FeatureFlags{LockdownMode:cfg.LockdownMode},
)
err=tsg.EnableToolsets(enabledToolsets,nil)

iferr!=nil {
returnnil,fmt.Errorf("failed to enable toolsets: %w",err)
}
// PRIORITY: If specific tools are configured, use tool-level registration
iflen(cfg.EnabledTools)>0 {
// Clean and validate tool names
enabledTools,invalidTools:=github.CleanTools(cfg.EnabledTools)

iflen(invalidTools)>0 {
fmt.Fprintf(os.Stderr,"Invalid tools ignored: %s\n",strings.Join(invalidTools,", "))
}

// Register all mcp functionality with the server
tsg.RegisterAll(ghServer)
// Register only the specified tools (bypasses toolset enablement)
err=tsg.RegisterSpecificTools(ghServer,enabledTools,cfg.ReadOnly)
iferr!=nil {
returnnil,fmt.Errorf("failed to register tools: %w",err)
}

ifcfg.DynamicToolsets {
dynamic:=github.InitDynamicToolset(ghServer,tsg,cfg.Translator)
dynamic.RegisterTools(ghServer)
// Still register resources and prompts from all toolsets to maintain functionality
for_,toolset:=rangetsg.Toolsets {
toolset.RegisterResourcesTemplates(ghServer)
toolset.RegisterPrompts(ghServer)
}
}else {
// EXISTING FLOW: Use toolset-based registration
err=tsg.EnableToolsets(enabledToolsets,nil)
iferr!=nil {
returnnil,fmt.Errorf("failed to enable toolsets: %w",err)
}

// Register all mcp functionality with the server
tsg.RegisterAll(ghServer)

// Dynamic toolsets only work if no specific tools configured
ifcfg.DynamicToolsets {
dynamic:=github.InitDynamicToolset(ghServer,tsg,cfg.Translator)
dynamic.RegisterTools(ghServer)
}
}

returnghServer,nil
Expand All@@ -197,6 +225,10 @@ type StdioServerConfig struct {
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration
EnabledToolsets []string

// EnabledTools is a list of specific tools to enable (takes priority over toolsets)
// When specified, only these tools will be registered, bypassing toolset enablement
EnabledTools []string

// Whether to enable dynamic toolsets
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#dynamic-tool-discovery
DynamicToolsetsbool
Expand DownExpand Up@@ -234,6 +266,7 @@ func RunStdioServer(cfg StdioServerConfig) error {
Host:cfg.Host,
Token:cfg.Token,
EnabledToolsets:cfg.EnabledToolsets,
EnabledTools:cfg.EnabledTools,
DynamicToolsets:cfg.DynamicToolsets,
ReadOnly:cfg.ReadOnly,
Translator:t,
Expand Down
27 changes: 27 additions & 0 deletionspkg/github/tools.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -525,3 +525,30 @@ func ContainsToolset(tools []string, toCheck string) bool {
}
return false
}

// CleanTools validates and cleans tool names:
// - Duplicates are removed from the result
// - Removes whitespaces
// - Validation of tool existence is done during registration
// Returns: (cleaned tools, invalid tools)
// Note: Invalid tools are identified during registration, so this function only cleans and deduplicates.
func CleanTools(toolNames []string) ([]string, []string) {
seen := make(map[string]bool)
result := make([]string, 0, len(toolNames))
invalid := make([]string, 0)

// Remove duplicates and trim whitespace
for _, tool := range toolNames {
trimmed := strings.TrimSpace(tool)
if trimmed == "" {
continue
}
if !seen[trimmed] {
seen[trimmed] = true
result = append(result, trimmed)
}
}

// Validation will happen during registration, so we return empty invalid list here
return result, invalid
}
59 changes: 59 additions & 0 deletionspkg/toolsets/toolsets.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -263,3 +263,62 @@ func (tg *ToolsetGroup) GetToolset(name string) (*Toolset, error) {
}
return toolset, nil
}

type ToolDoesNotExistError struct {
Name string
}

func (e *ToolDoesNotExistError) Error() string {
return fmt.Sprintf("tool %s does not exist", e.Name)
}

func NewToolDoesNotExistError(name string) *ToolDoesNotExistError {
return &ToolDoesNotExistError{Name: name}
}

// FindToolByName searches all toolsets (enabled or disabled) for a tool by name.
// Returns the tool, its parent toolset name, and an error if not found.
func (tg *ToolsetGroup) FindToolByName(toolName string) (*server.ServerTool, string, error) {
for toolsetName, toolset := range tg.Toolsets {
// Check read tools
for _, tool := range toolset.readTools {
if tool.Tool.Name == toolName {
return &tool, toolsetName, nil
}
}
// Check write tools
for _, tool := range toolset.writeTools {
if tool.Tool.Name == toolName {
return &tool, toolsetName, nil
}
}
}
return nil, "", NewToolDoesNotExistError(toolName)
}

// RegisterSpecificTools registers only the specified tools, bypassing toolset enablement.
// Respects read-only mode (skips write tools if readOnly=true).
// Returns error if any tool is not found.
func (tg *ToolsetGroup) RegisterSpecificTools(s *server.MCPServer, toolNames []string, readOnly bool) error {
for _, toolName := range toolNames {
tool, toolsetName, err := tg.FindToolByName(toolName)
if err != nil {
return fmt.Errorf("tool %s not found: %w", toolName, err)
}

// Check if it's a write tool and we're in read-only mode
// ReadOnlyHint should always be set, but add defensive check
if tool.Tool.Annotations.ReadOnlyHint != nil {
isWriteTool := !*tool.Tool.Annotations.ReadOnlyHint
if isWriteTool && readOnly {
// Skip write tools in read-only mode
continue
}
}

// Register the tool
s.AddTool(tool.Tool, tool.Handler)
_ = toolsetName // toolsetName is available for potential future use (logging, etc.)
}
return nil
}

[8]ページ先頭

©2009-2025 Movatter.jp