Skip to content

Commit b93afa5

Browse files
committed
enable/disable tools command
1 parent 8e7c5a4 commit b93afa5

File tree

7 files changed

+494
-4
lines changed

7 files changed

+494
-4
lines changed

README.md

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

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

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

cmd/docker-mcp/commands/root.go

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

8383
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",
65+
Short: "enable a tool",
66+
Args: cobra.ExactArgs(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 tool (optional, will auto-discover if not provided)")
72+
cmd.AddCommand(enableCmd)
73+
74+
var disableServerName string
75+
disableCmd := &cobra.Command{
76+
Use: "disable",
77+
Short: "disable a tool",
78+
Args: cobra.ExactArgs(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 tool (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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 serverTools ToolsConfig
13+
if err := yaml.Unmarshal(toolsYaml, &serverTools); err != nil {
14+
return ToolsConfig{}, err
15+
}
16+
17+
return serverTools, nil
18+
}

cmd/docker-mcp/tools/enable.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package tools
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"fmt"
7+
"sort"
8+
9+
"gopkg.in/yaml.v3"
10+
11+
"slices"
12+
13+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/catalog"
14+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/config"
15+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
16+
)
17+
18+
func Disable(ctx context.Context, docker docker.Client, toolNames []string, serverName string) error {
19+
return update(ctx, docker, nil, toolNames, serverName)
20+
}
21+
22+
func Enable(ctx context.Context, docker docker.Client, toolNames []string, serverName string) error {
23+
return update(ctx, docker, toolNames, nil, serverName)
24+
}
25+
26+
func findServerByTool(mcpCatalog catalog.Catalog, toolName string) (string, error) {
27+
for serverName, server := range mcpCatalog.Servers {
28+
for _, tool := range server.Tools {
29+
if tool.Name == toolName {
30+
return serverName, nil
31+
}
32+
}
33+
}
34+
return "", fmt.Errorf("tool %q not found in any server", toolName)
35+
}
36+
37+
func validateToolExistsInServer(mcpCatalog catalog.Catalog, serverName string, toolName string) error {
38+
if _, exists := mcpCatalog.Servers[serverName]; !exists {
39+
return fmt.Errorf("server %q not found in catalog", serverName)
40+
}
41+
42+
for _, tool := range mcpCatalog.Servers[serverName].Tools {
43+
if tool.Name == toolName {
44+
return nil
45+
}
46+
}
47+
return fmt.Errorf("tool %q not found in server %q", toolName, serverName)
48+
}
49+
50+
func update(ctx context.Context, docker docker.Client, add []string, remove []string, serverName string) error {
51+
toolsYAML, err := config.ReadTools(ctx, docker)
52+
if err != nil {
53+
return fmt.Errorf("reading tools: %w", err)
54+
}
55+
56+
toolsConfig, err := config.ParseToolsConfig(toolsYAML)
57+
if err != nil {
58+
return fmt.Errorf("parsing tools: %w", err)
59+
}
60+
61+
if toolsConfig.ServerTools == nil {
62+
toolsConfig.ServerTools = make(map[string][]string)
63+
}
64+
65+
mcpCatalog, err := catalog.Get(ctx)
66+
if err != nil {
67+
return fmt.Errorf("reading catalog: %w", err)
68+
}
69+
70+
for _, toolName := range add {
71+
var targetServerName string
72+
73+
if serverName != "" {
74+
if err := validateToolExistsInServer(mcpCatalog, serverName, toolName); err != nil {
75+
return err
76+
}
77+
targetServerName = serverName
78+
} else {
79+
discoveredServerName, err := findServerByTool(mcpCatalog, toolName)
80+
if err != nil {
81+
return err
82+
}
83+
targetServerName = discoveredServerName
84+
}
85+
86+
toolAlreadyEnabled := slices.Contains(toolsConfig.ServerTools[targetServerName], toolName)
87+
if toolAlreadyEnabled {
88+
continue
89+
}
90+
91+
toolsConfig.ServerTools[targetServerName] = append(toolsConfig.ServerTools[targetServerName], toolName)
92+
}
93+
94+
for _, toolName := range remove {
95+
var targetServerName string
96+
97+
if serverName != "" {
98+
if err := validateToolExistsInServer(mcpCatalog, serverName, toolName); err != nil {
99+
return err
100+
}
101+
targetServerName = serverName
102+
} else {
103+
discoveredServerName, err := findServerByTool(mcpCatalog, toolName)
104+
if err != nil {
105+
return err
106+
}
107+
targetServerName = discoveredServerName
108+
}
109+
110+
if _, exists := toolsConfig.ServerTools[targetServerName]; !exists {
111+
serverTools := mcpCatalog.Servers[targetServerName].Tools
112+
var allToolNames []string
113+
for _, tool := range serverTools {
114+
allToolNames = append(allToolNames, tool.Name)
115+
}
116+
117+
toolsConfig.ServerTools[targetServerName] = []string{}
118+
for _, tool := range allToolNames {
119+
if tool != toolName {
120+
toolsConfig.ServerTools[targetServerName] = append(toolsConfig.ServerTools[targetServerName], tool)
121+
}
122+
}
123+
continue
124+
}
125+
126+
tools := toolsConfig.ServerTools[targetServerName]
127+
newTools := make([]string, 0, len(tools))
128+
for _, tool := range tools {
129+
if tool != toolName {
130+
newTools = append(newTools, tool)
131+
}
132+
}
133+
toolsConfig.ServerTools[targetServerName] = newTools
134+
}
135+
136+
for serverName := range toolsConfig.ServerTools {
137+
sort.Strings(toolsConfig.ServerTools[serverName])
138+
}
139+
140+
var buf bytes.Buffer
141+
encoder := yaml.NewEncoder(&buf)
142+
encoder.SetIndent(2)
143+
if err := encoder.Encode(toolsConfig.ServerTools); err != nil {
144+
return fmt.Errorf("encoding tools: %w", err)
145+
}
146+
147+
if err := config.WriteTools(buf.Bytes()); err != nil {
148+
return fmt.Errorf("writing tools: %w", err)
149+
}
150+
151+
return nil
152+
}

0 commit comments

Comments
 (0)