Skip to content

Commit fd5dcbb

Browse files
committed
Checkpoint.
1 parent e00c594 commit fd5dcbb

File tree

8 files changed

+958
-27
lines changed

8 files changed

+958
-27
lines changed

cmd/docker-mcp/commands/feature.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
7+
"github.com/docker/cli/cli/command"
8+
"github.com/docker/cli/cli/config/configfile"
9+
"github.com/spf13/cobra"
10+
)
11+
12+
// featureCommand creates the `feature` command and its subcommands
13+
func featureCommand(dockerCli command.Cli) *cobra.Command {
14+
cmd := &cobra.Command{
15+
Use: "feature",
16+
Short: "Manage experimental features",
17+
Long: `Manage experimental features for Docker MCP Gateway.
18+
19+
Features are stored in your Docker configuration file (~/.docker/config.json)
20+
and control optional functionality that may change in future versions.`,
21+
}
22+
23+
cmd.AddCommand(
24+
featureEnableCommand(dockerCli),
25+
featureDisableCommand(dockerCli),
26+
featureListCommand(dockerCli),
27+
)
28+
29+
return cmd
30+
}
31+
32+
// featureEnableCommand creates the `feature enable` command
33+
func featureEnableCommand(dockerCli command.Cli) *cobra.Command {
34+
return &cobra.Command{
35+
Use: "enable <feature-name>",
36+
Short: "Enable an experimental feature",
37+
Long: `Enable an experimental feature.
38+
39+
Available features:
40+
configured-catalogs Allow gateway to use user-managed catalogs alongside Docker catalog`,
41+
Args: cobra.ExactArgs(1),
42+
RunE: func(cmd *cobra.Command, args []string) error {
43+
featureName := args[0]
44+
45+
// Validate feature name
46+
if !isKnownFeature(featureName) {
47+
return fmt.Errorf("unknown feature: %s\n\nAvailable features:\n configured-catalogs Allow gateway to use user-managed catalogs", featureName)
48+
}
49+
50+
// Enable the feature
51+
configFile := dockerCli.ConfigFile()
52+
if configFile.Features == nil {
53+
configFile.Features = make(map[string]string)
54+
}
55+
configFile.Features[featureName] = "enabled"
56+
57+
// Save the configuration
58+
if err := configFile.Save(); err != nil {
59+
return fmt.Errorf("failed to save configuration: %w", err)
60+
}
61+
62+
fmt.Printf("Feature '%s' enabled successfully.\n", featureName)
63+
64+
// Provide usage hints for configured-catalogs
65+
if featureName == "configured-catalogs" {
66+
fmt.Println("\nTo use configured catalogs with the gateway, run:")
67+
fmt.Println(" docker mcp gateway run --use-configured-catalogs")
68+
fmt.Println("\nTo create and manage catalogs, use:")
69+
fmt.Println(" docker mcp catalog create <name>")
70+
fmt.Println(" docker mcp catalog add <catalog> <server-name> <server-file>")
71+
}
72+
73+
return nil
74+
},
75+
}
76+
}
77+
78+
// featureDisableCommand creates the `feature disable` command
79+
func featureDisableCommand(dockerCli command.Cli) *cobra.Command {
80+
return &cobra.Command{
81+
Use: "disable <feature-name>",
82+
Short: "Disable an experimental feature",
83+
Long: "Disable an experimental feature that was previously enabled.",
84+
Args: cobra.ExactArgs(1),
85+
RunE: func(cmd *cobra.Command, args []string) error {
86+
featureName := args[0]
87+
88+
// Validate feature name
89+
if !isKnownFeature(featureName) {
90+
return fmt.Errorf("unknown feature: %s", featureName)
91+
}
92+
93+
// Disable the feature
94+
configFile := dockerCli.ConfigFile()
95+
if configFile.Features == nil {
96+
configFile.Features = make(map[string]string)
97+
}
98+
configFile.Features[featureName] = "disabled"
99+
100+
// Save the configuration
101+
if err := configFile.Save(); err != nil {
102+
return fmt.Errorf("failed to save configuration: %w", err)
103+
}
104+
105+
fmt.Printf("Feature '%s' disabled successfully.\n", featureName)
106+
return nil
107+
},
108+
}
109+
}
110+
111+
// featureListCommand creates the `feature list` command
112+
func featureListCommand(dockerCli command.Cli) *cobra.Command {
113+
return &cobra.Command{
114+
Use: "list",
115+
Short: "List all available features and their status",
116+
Long: "List all available experimental features and show whether they are enabled or disabled.",
117+
RunE: func(cmd *cobra.Command, args []string) error {
118+
configFile := dockerCli.ConfigFile()
119+
120+
fmt.Println("Available experimental features:")
121+
fmt.Println()
122+
123+
// Show all known features
124+
knownFeatures := []string{"configured-catalogs"}
125+
for _, feature := range knownFeatures {
126+
status := "disabled"
127+
if isFeatureEnabledFromCli(dockerCli, feature) {
128+
status = "enabled"
129+
}
130+
131+
fmt.Printf(" %-20s %s\n", feature, status)
132+
133+
// Add description for each feature
134+
switch feature {
135+
case "configured-catalogs":
136+
fmt.Printf(" %-20s %s\n", "", "Allow gateway to use user-managed catalogs alongside Docker catalog")
137+
}
138+
fmt.Println()
139+
}
140+
141+
// Show any other features in config that we don't know about
142+
if configFile.Features != nil {
143+
unknownFeatures := make([]string, 0)
144+
for feature := range configFile.Features {
145+
if !isKnownFeature(feature) {
146+
unknownFeatures = append(unknownFeatures, feature)
147+
}
148+
}
149+
150+
if len(unknownFeatures) > 0 {
151+
fmt.Println("Unknown features in configuration:")
152+
for _, feature := range unknownFeatures {
153+
status := configFile.Features[feature]
154+
fmt.Printf(" %-20s %s (unknown)\n", feature, status)
155+
}
156+
}
157+
}
158+
159+
return nil
160+
},
161+
}
162+
}
163+
164+
// isFeatureEnabledFromCli checks if a feature is enabled using the CLI interface
165+
func isFeatureEnabledFromCli(dockerCli command.Cli, feature string) bool {
166+
configFile := dockerCli.ConfigFile()
167+
return isFeatureEnabledFromConfig(configFile, feature)
168+
}
169+
170+
// isFeatureEnabledFromConfig checks if a feature is enabled from a config file
171+
func isFeatureEnabledFromConfig(configFile *configfile.ConfigFile, feature string) bool {
172+
if configFile.Features == nil {
173+
return false
174+
}
175+
176+
value, exists := configFile.Features[feature]
177+
if !exists {
178+
return false
179+
}
180+
181+
// Handle both boolean string values and "enabled"/"disabled" strings
182+
if value == "enabled" {
183+
return true
184+
}
185+
if value == "disabled" {
186+
return false
187+
}
188+
189+
// Fallback to parsing as boolean
190+
enabled, err := strconv.ParseBool(value)
191+
return err == nil && enabled
192+
}
193+
194+
// isKnownFeature checks if the feature name is valid
195+
func isKnownFeature(feature string) bool {
196+
knownFeatures := []string{
197+
"configured-catalogs",
198+
}
199+
200+
for _, known := range knownFeatures {
201+
if feature == known {
202+
return true
203+
}
204+
}
205+
return false
206+
}

0 commit comments

Comments
 (0)