Skip to content

Commit 5722077

Browse files
authored
Merge pull request #62 from docker/enable-disable-tools
Tools filtering
2 parents e08a3be + e4e0b8d commit 5722077

26 files changed

+849
-44
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ The MCP CLI uses several configuration files:
184184

185185
- **`docker-mcp.yaml`**: Server catalog defining available MCP servers
186186
- **`registry.yaml`**: Registry of enabled servers
187-
- **`config.yaml`**: Gateway configuration and options
187+
- **`config.yaml`**: Configuration per server
188+
- **`tools.yaml`**: Enabled tools per server
188189

189190
Configuration files are typically stored in `~/.docker/mcp/`. This is in this directory that Docker Desktop's
190191
MCP Toolkit with store its configuration.

cmd/docker-mcp/backup/dump.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ func Dump(ctx context.Context, docker docker.Client) ([]byte, error) {
2626
return nil, err
2727
}
2828

29+
toolsConfig, err := config.ReadTools(ctx, docker)
30+
if err != nil {
31+
return nil, err
32+
}
33+
2934
catalogConfig, err := catalog.ReadConfig()
3035
if err != nil {
3136
return nil, err
@@ -74,6 +79,7 @@ func Dump(ctx context.Context, docker docker.Client) ([]byte, error) {
7479
Registry: string(registryContent),
7580
Catalog: string(catalogContent),
7681
CatalogFiles: catalogFiles,
82+
Tools: string(toolsConfig),
7783
Secrets: secrets,
7884
Policy: policy,
7985
}

cmd/docker-mcp/backup/restore.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ func Restore(ctx context.Context, backupData []byte) error {
2121
if err := config.WriteRegistry([]byte(backup.Registry)); err != nil {
2222
return err
2323
}
24+
if err := config.WriteTools([]byte(backup.Tools)); err != nil {
25+
return err
26+
}
2427

2528
catalogBefore, err := catalog.ReadConfig()
2629
if err != nil {

cmd/docker-mcp/backup/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type Backup struct {
77
Registry string `json:"registry"`
88
Catalog string `json:"catalog"`
99
CatalogFiles map[string]string `json:"catalogFiles"`
10+
Tools string `json:"tools"`
1011
Secrets []desktop.Secret `json:"secrets"`
1112
Policy string `json:"policy"`
1213
}

cmd/docker-mcp/commands/gateway.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func gatewayCommand(docker docker.Client) *cobra.Command {
2222
var additionalCatalogs []string
2323
var additionalRegistries []string
2424
var additionalConfigs []string
25+
var additionalToolsConfig []string
2526
if os.Getenv("DOCKER_MCP_IN_CONTAINER") == "1" {
2627
// In-container.
2728
options = gateway.Config{
@@ -43,6 +44,7 @@ func gatewayCommand(docker docker.Client) *cobra.Command {
4344
CatalogPath: []string{"docker-mcp.yaml"},
4445
RegistryPath: []string{"registry.yaml"},
4546
ConfigPath: []string{"config.yaml"},
47+
ToolsPath: []string{"tools.yaml"},
4648
SecretsPath: "docker-desktop",
4749
Options: gateway.Options{
4850
Cpus: 1,
@@ -81,6 +83,7 @@ func gatewayCommand(docker docker.Client) *cobra.Command {
8183
options.CatalogPath = append(options.CatalogPath, additionalCatalogs...)
8284
options.RegistryPath = append(options.RegistryPath, additionalRegistries...)
8385
options.ConfigPath = append(options.ConfigPath, additionalConfigs...)
86+
options.ToolsPath = append(options.ToolsPath, additionalToolsConfig...)
8487

8588
return gateway.NewGateway(options, docker).Run(cmd.Context())
8689
},
@@ -93,6 +96,8 @@ func gatewayCommand(docker docker.Client) *cobra.Command {
9396
runCmd.Flags().StringSliceVar(&additionalRegistries, "additional-registry", nil, "Additional registry paths to merge with the default registry.yaml")
9497
runCmd.Flags().StringSliceVar(&options.ConfigPath, "config", options.ConfigPath, "Paths to the config files (absolute or relative to ~/.docker/mcp/)")
9598
runCmd.Flags().StringSliceVar(&additionalConfigs, "additional-config", nil, "Additional config paths to merge with the default config.yaml")
99+
runCmd.Flags().StringSliceVar(&options.ToolsPath, "tools-config", options.ToolsPath, "Paths to the tools files (absolute or relative to ~/.docker/mcp/)")
100+
runCmd.Flags().StringSliceVar(&additionalToolsConfig, "additional-tools-config", nil, "Additional tools paths to merge with the default tools.yaml")
96101
runCmd.Flags().StringVar(&options.SecretsPath, "secrets", options.SecretsPath, "Colon separated paths to search for secrets. Can be `docker-desktop` or a path to a .env file (default to using Docker Desktop's secrets API)")
97102
runCmd.Flags().StringSliceVar(&options.ToolNames, "tools", options.ToolNames, "List of tools to enable")
98103
runCmd.Flags().StringArrayVar(&options.Interceptors, "interceptor", options.Interceptors, "List of interceptors to use (format: when:type:path, e.g. 'before:exec:/bin/path')")

cmd/docker-mcp/commands/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func Root(ctx context.Context, cwd string, dockerCli command.Cli) *cobra.Command
7878
cmd.AddCommand(policyCommand())
7979
cmd.AddCommand(secretCommand(dockerClient))
8080
cmd.AddCommand(serverCommand(dockerClient))
81-
cmd.AddCommand(toolsCommand())
81+
cmd.AddCommand(toolsCommand(dockerClient))
8282
cmd.AddCommand(versionCommand())
8383

8484
if os.Getenv("DOCKER_MCP_SHOW_HIDDEN") == "1" {

cmd/docker-mcp/commands/tools.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package commands
33
import (
44
"github.com/spf13/cobra"
55

6+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
67
"github.com/docker/mcp-gateway/cmd/docker-mcp/tools"
78
)
89

9-
func toolsCommand() *cobra.Command {
10+
func toolsCommand(docker docker.Client) *cobra.Command {
1011
cmd := &cobra.Command{
1112
Use: "tools",
12-
Short: "List/count/call MCP tools",
13+
Short: "Manage tools",
1314
}
1415

1516
var (
@@ -58,5 +59,29 @@ func toolsCommand() *cobra.Command {
5859
},
5960
})
6061

62+
var enableServerName string
63+
enableCmd := &cobra.Command{
64+
Use: "enable [tool1] [tool2] ...",
65+
Short: "enable one or more tools",
66+
Args: cobra.MinimumNArgs(1),
67+
RunE: func(cmd *cobra.Command, args []string) error {
68+
return tools.Enable(cmd.Context(), docker, args, enableServerName)
69+
},
70+
}
71+
enableCmd.Flags().StringVar(&enableServerName, "server", "", "Specify which server provides the tools (optional, will auto-discover if not provided)")
72+
cmd.AddCommand(enableCmd)
73+
74+
var disableServerName string
75+
disableCmd := &cobra.Command{
76+
Use: "disable [tool1] [tool2] ...",
77+
Short: "disable one or more tools",
78+
Args: cobra.MinimumNArgs(1),
79+
RunE: func(cmd *cobra.Command, args []string) error {
80+
return tools.Disable(cmd.Context(), docker, args, disableServerName)
81+
},
82+
}
83+
disableCmd.Flags().StringVar(&disableServerName, "server", "", "Specify which server provides the tools (optional, will auto-discover if not provided)")
84+
cmd.AddCommand(disableCmd)
85+
6186
return cmd
6287
}

cmd/docker-mcp/internal/config/readwrite.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import (
1111
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/user"
1212
)
1313

14+
func ReadTools(ctx context.Context, docker docker.Client) ([]byte, error) {
15+
return ReadConfigFile(ctx, docker, "tools.yaml")
16+
}
17+
1418
func ReadConfig(ctx context.Context, docker docker.Client) ([]byte, error) {
1519
return ReadConfigFile(ctx, docker, "config.yaml")
1620
}
@@ -37,6 +41,10 @@ func ReadCatalogFile(name string) ([]byte, error) {
3741
return readFileOrEmpty(path)
3842
}
3943

44+
func WriteTools(content []byte) error {
45+
return writeConfigFile("tools.yaml", content)
46+
}
47+
4048
func WriteConfig(content []byte) error {
4149
return writeConfigFile("config.yaml", content)
4250
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package config
2+
3+
import (
4+
"gopkg.in/yaml.v3"
5+
)
6+
7+
type ToolsConfig struct {
8+
ServerTools map[string][]string `yaml:",inline"`
9+
}
10+
11+
func ParseToolsConfig(toolsYaml []byte) (ToolsConfig, error) {
12+
var toolsConfig ToolsConfig
13+
if err := yaml.Unmarshal(toolsYaml, &toolsConfig); err != nil {
14+
return ToolsConfig{}, err
15+
}
16+
17+
if toolsConfig.ServerTools == nil {
18+
toolsConfig.ServerTools = make(map[string][]string)
19+
}
20+
21+
return toolsConfig, nil
22+
}

cmd/docker-mcp/internal/gateway/capabilitites.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"runtime"
7+
"slices"
78
"strings"
89
"sync"
910

@@ -51,7 +52,7 @@ func (g *Gateway) listCapabilities(ctx context.Context, configuration Configurat
5152
logf(" > Can't list tools %s: %s", serverConfig.Name, err)
5253
} else {
5354
for _, tool := range tools.Tools {
54-
if !isToolEnabled(serverConfig.Name, serverConfig.Spec.Image, tool.Name, g.ToolNames) {
55+
if !isToolEnabled(configuration, serverConfig.Name, serverConfig.Spec.Image, tool.Name, g.ToolNames) {
5556
continue
5657
}
5758
capabilities.Tools = append(capabilities.Tools, server.ServerTool{
@@ -120,7 +121,7 @@ func (g *Gateway) listCapabilities(ctx context.Context, configuration Configurat
120121
var capabilities Capabilities
121122

122123
for _, tool := range *toolGroup {
123-
if !isToolEnabled(serverName, "", tool.Name, g.ToolNames) {
124+
if !isToolEnabled(configuration, serverName, "", tool.Name, g.ToolNames) {
124125
continue
125126
}
126127

@@ -188,9 +189,14 @@ func (c *Capabilities) PromptNames() []string {
188189
return names
189190
}
190191

191-
func isToolEnabled(serverName, serverImage, toolName string, enabledTools []string) bool {
192+
func isToolEnabled(configuration Configuration, serverName, serverImage, toolName string, enabledTools []string) bool {
192193
if len(enabledTools) == 0 {
193-
return true
194+
tools, exists := configuration.tools.ServerTools[serverName]
195+
if !exists {
196+
return true
197+
}
198+
199+
return slices.Contains(tools, toolName)
194200
}
195201

196202
for _, enabled := range enabledTools {

0 commit comments

Comments
 (0)