Skip to content

Commit 21bf425

Browse files
committed
Extact each top level command
Signed-off-by: David Gageot <[email protected]>
1 parent 5c3eb27 commit 21bf425

File tree

6 files changed

+462
-0
lines changed

6 files changed

+462
-0
lines changed

cmd/docker-mcp/commands/config.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/docker/mcp-gateway/cmd/docker-mcp/backup"
11+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/config"
12+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
13+
)
14+
15+
func configCommand(docker docker.Client) *cobra.Command {
16+
cmd := &cobra.Command{
17+
Use: "config",
18+
Short: "Manage the configuration",
19+
}
20+
21+
cmd.AddCommand(&cobra.Command{
22+
Use: "read",
23+
Short: "Read the configuration",
24+
Args: cobra.NoArgs,
25+
RunE: func(cmd *cobra.Command, _ []string) error {
26+
content, err := config.ReadConfig(cmd.Context(), docker)
27+
if err != nil {
28+
return err
29+
}
30+
_, _ = cmd.OutOrStdout().Write(content)
31+
return nil
32+
},
33+
})
34+
cmd.AddCommand(&cobra.Command{
35+
Use: "write",
36+
Short: "Write the configuration",
37+
Args: cobra.ExactArgs(1),
38+
RunE: func(_ *cobra.Command, args []string) error {
39+
return config.WriteConfig([]byte(args[0]))
40+
},
41+
})
42+
cmd.AddCommand(&cobra.Command{
43+
Use: "reset",
44+
Short: "Reset the configuration",
45+
Args: cobra.NoArgs,
46+
RunE: func(*cobra.Command, []string) error {
47+
return config.WriteConfig(nil)
48+
},
49+
})
50+
cmd.AddCommand(&cobra.Command{
51+
Use: "dump",
52+
Short: "Dump the whole configuration",
53+
Args: cobra.NoArgs,
54+
Hidden: true,
55+
RunE: func(cmd *cobra.Command, _ []string) error {
56+
out, err := backup.Dump(cmd.Context(), docker)
57+
if err != nil {
58+
return err
59+
}
60+
_, _ = cmd.OutOrStdout().Write(out)
61+
return nil
62+
},
63+
})
64+
cmd.AddCommand(&cobra.Command{
65+
Use: "restore",
66+
Short: "Restore the whole configuration",
67+
Args: cobra.ExactArgs(1),
68+
Hidden: true,
69+
RunE: func(cmd *cobra.Command, args []string) error {
70+
path := args[0]
71+
72+
var backupData []byte
73+
if path == "-" {
74+
var err error
75+
backupData, err = io.ReadAll(os.Stdin)
76+
if err != nil {
77+
return fmt.Errorf("reading from stdin: %w", err)
78+
}
79+
} else {
80+
var err error
81+
backupData, err = os.ReadFile(path)
82+
if err != nil {
83+
return err
84+
}
85+
}
86+
87+
return backup.Restore(cmd.Context(), backupData)
88+
},
89+
})
90+
91+
return cmd
92+
}

cmd/docker-mcp/commands/gateway.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package commands
2+
3+
import (
4+
"os"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/docker/mcp-gateway/cmd/docker-mcp/catalog"
9+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
10+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/gateway"
11+
)
12+
13+
func gatewayCommand(docker docker.Client) *cobra.Command {
14+
cmd := &cobra.Command{
15+
Use: "gateway",
16+
Short: "Manage the MCP Server gateway",
17+
}
18+
19+
// Have different defaults for the on-host gateway and the in-container gateway.
20+
var options gateway.Config
21+
if os.Getenv("DOCKER_MCP_IN_CONTAINER") == "1" {
22+
// In-container.
23+
options = gateway.Config{
24+
CatalogPath: catalog.DockerCatalogURL,
25+
SecretsPath: "docker-desktop:/run/secrets/mcp_secret:/.env",
26+
Options: gateway.Options{
27+
Cpus: 1,
28+
Memory: "2Gb",
29+
Transport: "stdio",
30+
Port: 8811,
31+
LogCalls: true,
32+
BlockSecrets: true,
33+
VerifySignatures: true,
34+
Verbose: true,
35+
},
36+
}
37+
} else {
38+
// On-host.
39+
options = gateway.Config{
40+
CatalogPath: "docker-mcp.yaml",
41+
RegistryPath: "registry.yaml",
42+
ConfigPath: "config.yaml",
43+
SecretsPath: "docker-desktop",
44+
Options: gateway.Options{
45+
Cpus: 1,
46+
Memory: "2Gb",
47+
Transport: "stdio",
48+
LogCalls: true,
49+
BlockSecrets: true,
50+
Watch: true,
51+
},
52+
}
53+
}
54+
55+
runCmd := &cobra.Command{
56+
Use: "run",
57+
Short: "Run the gateway",
58+
Args: cobra.NoArgs,
59+
RunE: func(cmd *cobra.Command, _ []string) error {
60+
return gateway.NewGateway(options, docker).Run(cmd.Context())
61+
},
62+
}
63+
64+
runCmd.Flags().StringSliceVar(&options.ServerNames, "servers", nil, "names of the servers to enable (if non empty, ignore --registry flag)")
65+
runCmd.Flags().StringVar(&options.CatalogPath, "catalog", options.CatalogPath, "path to the docker-mcp.yaml catalog (absolute or relative to ~/.docker/mcp/catalogs/)")
66+
runCmd.Flags().StringVar(&options.RegistryPath, "registry", options.RegistryPath, "path to the registry.yaml (absolute or relative to ~/.docker/mcp/)")
67+
runCmd.Flags().StringVar(&options.ConfigPath, "config", options.ConfigPath, "path to the config.yaml (absolute or relative to ~/.docker/mcp/)")
68+
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 Deskop's secrets API)")
69+
runCmd.Flags().StringSliceVar(&options.ToolNames, "tools", options.ToolNames, "List of tools to enable")
70+
runCmd.Flags().StringArrayVar(&options.Interceptors, "interceptor", options.Interceptors, "List of interceptors to use (format: when:type:path, e.g. 'before:exec:/bin/path')")
71+
runCmd.Flags().IntVar(&options.Port, "port", options.Port, "TCP port to listen on (default is to listen on stdio)")
72+
runCmd.Flags().StringVar(&options.Transport, "transport", options.Transport, "stdio, sse or streaming (default is stdio)")
73+
runCmd.Flags().BoolVar(&options.LogCalls, "log-calls", options.LogCalls, "Log calls to the tools")
74+
runCmd.Flags().BoolVar(&options.BlockSecrets, "block-secrets", options.BlockSecrets, "Block secrets from being/received sent to/from tools")
75+
runCmd.Flags().BoolVar(&options.BlockNetwork, "block-network", options.BlockNetwork, "Block tools from accessing forbidden network resources")
76+
runCmd.Flags().BoolVar(&options.VerifySignatures, "verify-signatures", options.VerifySignatures, "Verify signatures of the server images")
77+
runCmd.Flags().BoolVar(&options.DryRun, "dry-run", options.DryRun, "Start the gateway but do not listen for connections (useful for testing the configuration)")
78+
runCmd.Flags().BoolVar(&options.Verbose, "verbose", options.Verbose, "Verbose output")
79+
runCmd.Flags().BoolVar(&options.LongLived, "long-lived", options.LongLived, "Containers are long-lived and will not be removed until the gateway is stopped, useful for stateful servers")
80+
runCmd.Flags().BoolVar(&options.DebugDNS, "debug-dns", options.DebugDNS, "Debug DNS resolution")
81+
runCmd.Flags().BoolVar(&options.Watch, "watch", options.Watch, "Watch for changes and reconfigure the gateway")
82+
runCmd.Flags().IntVar(&options.Cpus, "cpus", options.Cpus, "CPUs allocated to each MCP Server (default is 1)")
83+
runCmd.Flags().StringVar(&options.Memory, "memory", options.Memory, "Memory allocated to each MCP Server (default is 2Gb)")
84+
runCmd.Flags().BoolVar(&options.Static, "static", options.Static, "Enable static mode (aka pre-started servers)")
85+
86+
cmd.AddCommand(runCmd)
87+
88+
return cmd
89+
}

cmd/docker-mcp/commands/root.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package commands
2+
3+
import (
4+
"context"
5+
"os"
6+
7+
"github.com/docker/cli/cli-plugins/plugin"
8+
"github.com/docker/cli/cli/command"
9+
"github.com/spf13/cobra"
10+
11+
"github.com/docker/mcp-gateway/cmd/docker-mcp/catalog"
12+
"github.com/docker/mcp-gateway/cmd/docker-mcp/client"
13+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/desktop"
14+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
15+
"github.com/docker/mcp-gateway/cmd/docker-mcp/oauth"
16+
"github.com/docker/mcp-gateway/cmd/docker-mcp/secret-management/policy"
17+
"github.com/docker/mcp-gateway/cmd/docker-mcp/secret-management/secret"
18+
"github.com/docker/mcp-gateway/cmd/docker-mcp/version"
19+
)
20+
21+
// Note: We use a custom help template to make it more brief.
22+
const helpTemplate = `Docker MCP Toolkit's CLI - Manage your MCP servers and clients.
23+
{{if .UseLine}}
24+
Usage: {{.UseLine}}
25+
{{end}}{{if .HasAvailableLocalFlags}}
26+
Flags:
27+
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
28+
{{end}}{{if .HasAvailableSubCommands}}
29+
Available Commands:
30+
{{range .Commands}}{{if (or .IsAvailableCommand)}} {{rpad .Name .NamePadding }} {{.Short}}
31+
{{end}}{{end}}{{end}}{{if .HasExample}}
32+
33+
Examples:
34+
{{.Example}}{{end}}
35+
`
36+
37+
// Root returns the root command for the init plugin
38+
func Root(ctx context.Context, cwd string, dockerCli command.Cli) *cobra.Command {
39+
cmd := &cobra.Command{
40+
Use: "mcp [OPTIONS]",
41+
TraverseChildren: true,
42+
CompletionOptions: cobra.CompletionOptions{
43+
DisableDefaultCmd: false,
44+
HiddenDefaultCmd: true,
45+
},
46+
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
47+
cmd.SetContext(ctx)
48+
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
49+
return err
50+
}
51+
52+
if os.Getenv("DOCKER_MCP_IN_CONTAINER") != "1" {
53+
runningInDockerCE, err := docker.RunningInDockerCE(ctx, dockerCli)
54+
if err != nil {
55+
return err
56+
}
57+
58+
if !runningInDockerCE {
59+
return desktop.CheckFeatureIsEnabled(ctx, "enableDockerMCPToolkit", "Docker MCP Toolkit")
60+
}
61+
}
62+
63+
return nil
64+
},
65+
Version: version.Version,
66+
}
67+
cmd.SetVersionTemplate("{{.Version}}\n")
68+
cmd.Flags().BoolP("version", "v", false, "Print version information and quit")
69+
cmd.SetHelpTemplate(helpTemplate)
70+
71+
_ = cmd.RegisterFlagCompletionFunc("mcp", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
72+
return []string{"--help"}, cobra.ShellCompDirectiveNoFileComp
73+
})
74+
75+
dockerClient := docker.NewClient(dockerCli)
76+
77+
cmd.AddCommand(secret.NewSecretsCmd(dockerClient))
78+
cmd.AddCommand(policy.NewPolicyCmd())
79+
cmd.AddCommand(oauth.NewOAuthCmd())
80+
cmd.AddCommand(client.NewClientCmd(cwd))
81+
cmd.AddCommand(catalog.NewCatalogCmd())
82+
cmd.AddCommand(versionCommand())
83+
cmd.AddCommand(gatewayCommand(dockerClient))
84+
cmd.AddCommand(configCommand(dockerClient))
85+
cmd.AddCommand(serverCommand(dockerClient))
86+
cmd.AddCommand(toolsCommand())
87+
88+
if os.Getenv("DOCKER_MCP_SHOW_HIDDEN") == "1" {
89+
unhideHiddenCommands(cmd)
90+
}
91+
92+
return cmd
93+
}
94+
95+
func unhideHiddenCommands(cmd *cobra.Command) {
96+
// Unhide all commands that are marked as hidden
97+
for _, c := range cmd.Commands() {
98+
c.Hidden = false
99+
unhideHiddenCommands(c)
100+
}
101+
}

cmd/docker-mcp/commands/server.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package commands
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/config"
11+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/docker"
12+
"github.com/docker/mcp-gateway/cmd/docker-mcp/server"
13+
)
14+
15+
func serverCommand(docker docker.Client) *cobra.Command {
16+
cmd := &cobra.Command{
17+
Use: "server",
18+
Short: "Manage servers",
19+
}
20+
21+
var outputJSON bool
22+
lsCommand := &cobra.Command{
23+
Use: "list",
24+
Aliases: []string{"ls"},
25+
Short: "list enabled servers",
26+
Args: cobra.NoArgs,
27+
RunE: func(cmd *cobra.Command, _ []string) error {
28+
list, err := server.List(cmd.Context(), docker)
29+
if err != nil {
30+
return err
31+
}
32+
33+
if outputJSON {
34+
buf, err := json.Marshal(list)
35+
if err != nil {
36+
return err
37+
}
38+
_, _ = cmd.OutOrStdout().Write(buf)
39+
} else if len(list) == 0 {
40+
fmt.Fprintln(cmd.OutOrStdout(), "No server is enabled")
41+
} else {
42+
fmt.Fprintln(cmd.OutOrStdout(), strings.Join(list, ", "))
43+
}
44+
45+
return nil
46+
},
47+
Hidden: true,
48+
}
49+
lsCommand.Flags().BoolVar(&outputJSON, "json", false, "Output in JSON format")
50+
cmd.AddCommand(lsCommand)
51+
52+
cmd.AddCommand(&cobra.Command{
53+
Use: "enable",
54+
Aliases: []string{"add"},
55+
Short: "Enable a server or multiple servers",
56+
Args: cobra.MinimumNArgs(1),
57+
RunE: func(cmd *cobra.Command, args []string) error {
58+
return server.Enable(cmd.Context(), docker, args)
59+
},
60+
})
61+
cmd.AddCommand(&cobra.Command{
62+
Use: "disable",
63+
Aliases: []string{"remove", "rm"},
64+
Short: "Disable a server or multiple servers",
65+
Args: cobra.MinimumNArgs(1),
66+
RunE: func(cmd *cobra.Command, args []string) error {
67+
return server.Disable(cmd.Context(), docker, args)
68+
},
69+
})
70+
cmd.AddCommand(&cobra.Command{
71+
Use: "inspect",
72+
Short: "Get information about a server",
73+
Args: cobra.ExactArgs(1),
74+
RunE: func(cmd *cobra.Command, args []string) error {
75+
info, err := server.Inspect(cmd.Context(), args[0])
76+
if err != nil {
77+
return err
78+
}
79+
80+
buf, err := info.ToJSON()
81+
if err != nil {
82+
return err
83+
}
84+
85+
_, _ = cmd.OutOrStdout().Write(buf)
86+
return nil
87+
},
88+
})
89+
cmd.AddCommand(&cobra.Command{
90+
Use: "reset",
91+
Short: "Disable all the servers",
92+
Args: cobra.NoArgs,
93+
RunE: func(*cobra.Command, []string) error {
94+
return config.WriteRegistry(nil)
95+
},
96+
})
97+
98+
return cmd
99+
}

0 commit comments

Comments
 (0)