Skip to content

Commit 0e317b6

Browse files
All config files should be ready for merging
* just keep this consistent across different filetypes (catalog/registry/config)
1 parent c6d95ad commit 0e317b6

File tree

3 files changed

+101
-49
lines changed

3 files changed

+101
-49
lines changed

cmd/docker-mcp/commands/gateway.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ func gatewayCommand(docker docker.Client) *cobra.Command {
1919
// Have different defaults for the on-host gateway and the in-container gateway.
2020
var options gateway.Config
2121
var additionalCatalogs []string
22+
var additionalRegistries []string
23+
var additionalConfigs []string
2224
if os.Getenv("DOCKER_MCP_IN_CONTAINER") == "1" {
2325
// In-container.
2426
options = gateway.Config{
@@ -38,8 +40,8 @@ func gatewayCommand(docker docker.Client) *cobra.Command {
3840
// On-host.
3941
options = gateway.Config{
4042
CatalogPath: []string{"docker-mcp.yaml"},
41-
RegistryPath: "registry.yaml",
42-
ConfigPath: "config.yaml",
43+
RegistryPath: []string{"registry.yaml"},
44+
ConfigPath: []string{"config.yaml"},
4345
SecretsPath: "docker-desktop",
4446
Options: gateway.Options{
4547
Cpus: 1,
@@ -63,15 +65,19 @@ func gatewayCommand(docker docker.Client) *cobra.Command {
6365

6466
// Append additional catalogs to the main catalog path
6567
options.CatalogPath = append(options.CatalogPath, additionalCatalogs...)
68+
options.RegistryPath = append(options.RegistryPath, additionalRegistries...)
69+
options.ConfigPath = append(options.ConfigPath, additionalConfigs...)
6670
return gateway.NewGateway(options, docker).Run(cmd.Context())
6771
},
6872
}
6973

7074
runCmd.Flags().StringSliceVar(&options.ServerNames, "servers", nil, "names of the servers to enable (if non empty, ignore --registry flag)")
7175
runCmd.Flags().StringSliceVar(&options.CatalogPath, "catalog", options.CatalogPath, "paths to docker catalogs (absolute or relative to ~/.docker/mcp/catalogs/)")
7276
runCmd.Flags().StringSliceVar(&additionalCatalogs, "additional-catalog", nil, "additional catalog paths to append to the default catalogs")
73-
runCmd.Flags().StringVar(&options.RegistryPath, "registry", options.RegistryPath, "path to the registry.yaml (absolute or relative to ~/.docker/mcp/)")
74-
runCmd.Flags().StringVar(&options.ConfigPath, "config", options.ConfigPath, "path to the config.yaml (absolute or relative to ~/.docker/mcp/)")
77+
runCmd.Flags().StringSliceVar(&options.RegistryPath, "registry", options.RegistryPath, "paths to the registry files (absolute or relative to ~/.docker/mcp/)")
78+
runCmd.Flags().StringSliceVar(&additionalRegistries, "additional-registry", nil, "additional registry paths to merge with the default registry.yaml")
79+
runCmd.Flags().StringSliceVar(&options.ConfigPath, "config", options.ConfigPath, "paths to the config files (absolute or relative to ~/.docker/mcp/)")
80+
runCmd.Flags().StringSliceVar(&additionalConfigs, "additional-config", nil, "additional config paths to merge with the default config.yaml")
7581
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)")
7682
runCmd.Flags().StringSliceVar(&options.ToolNames, "tools", options.ToolNames, "List of tools to enable")
7783
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/internal/gateway/config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ type Config struct {
44
Options
55
ServerNames []string
66
CatalogPath []string
7-
ConfigPath string
8-
RegistryPath string
7+
ConfigPath []string
8+
RegistryPath []string
99
SecretsPath string
1010
}
1111

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

Lines changed: 89 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ func (c *Configuration) Find(serverName string) (*catalog.ServerConfig, *map[str
8888
type FileBasedConfiguration struct {
8989
CatalogPath []string
9090
ServerNames []string // Takes precedence over the RegistryPath
91-
RegistryPath string
92-
ConfigPath string
91+
RegistryPath []string
92+
ConfigPath []string
9393
SecretsPath string // Optional, if not set, use Docker Desktop's secrets API
9494
Watch bool
9595

@@ -105,17 +105,28 @@ func (c *FileBasedConfiguration) Read(ctx context.Context) (Configuration, chan
105105
return configuration, nil, func() error { return nil }, nil
106106
}
107107

108-
var registryPath string
108+
var registryPaths []string
109109
if len(c.ServerNames) == 0 {
110-
registryPath, err = config.FilePath(c.RegistryPath)
111-
if err != nil {
112-
return Configuration{}, nil, nil, err
110+
for _, path := range c.RegistryPath {
111+
if path != "" {
112+
registryPath, err := config.FilePath(path)
113+
if err != nil {
114+
return Configuration{}, nil, nil, err
115+
}
116+
registryPaths = append(registryPaths, registryPath)
117+
}
113118
}
114119
}
115120

116-
configPath, err := config.FilePath(c.ConfigPath)
117-
if err != nil {
118-
return Configuration{}, nil, nil, err
121+
var configPaths []string
122+
for _, path := range c.ConfigPath {
123+
if path != "" {
124+
configPath, err := config.FilePath(path)
125+
if err != nil {
126+
return Configuration{}, nil, nil, err
127+
}
128+
configPaths = append(configPaths, configPath)
129+
}
119130
}
120131

121132
watcher, err := fsnotify.NewWatcher()
@@ -142,29 +153,30 @@ func (c *FileBasedConfiguration) Read(ctx context.Context) (Configuration, chan
142153
}
143154
}
144155

145-
if configuration, err := c.readOnce(ctx); err == nil {
146-
updates <- configuration
147-
}
148-
case err, ok := <-watcher.Errors:
149-
if !ok {
150-
return
156+
configuration, err := c.readOnce(ctx)
157+
if err != nil {
158+
log("Error reading configuration:", err)
159+
continue
151160
}
152-
logf("watch error: %s", err)
161+
162+
updates <- configuration
163+
164+
case <-ctx.Done():
165+
return
153166
}
154167
}
155168
}()
156169

157-
if registryPath != "" {
158-
log("- Watching registry at", registryPath)
159-
if err := watcher.Add(registryPath); err != nil {
160-
_ = watcher.Close()
170+
// Add all registry paths to watcher
171+
for _, path := range registryPaths {
172+
if err := watcher.Add(path); err != nil && !os.IsNotExist(err) {
161173
return Configuration{}, nil, nil, err
162174
}
163175
}
164-
if configPath != "" {
165-
log("- Watching config at", configPath)
166-
if err := watcher.Add(configPath); err != nil {
167-
_ = watcher.Close()
176+
177+
// Add all config paths to watcher
178+
for _, path := range configPaths {
179+
if err := watcher.Add(path); err != nil && !os.IsNotExist(err) {
168180
return Configuration{}, nil, nil, err
169181
}
170182
}
@@ -239,41 +251,75 @@ func (c *FileBasedConfiguration) readCatalog(ctx context.Context) (catalog.Catal
239251
}
240252

241253
func (c *FileBasedConfiguration) readRegistry(ctx context.Context) (config.Registry, error) {
242-
if c.RegistryPath == "" {
254+
if len(c.RegistryPath) == 0 {
243255
return config.Registry{}, nil
244256
}
245257

246-
log(" - Reading registry from", c.RegistryPath)
247-
yaml, err := config.ReadConfigFile(ctx, c.docker, c.RegistryPath)
248-
if err != nil {
249-
return config.Registry{}, fmt.Errorf("reading registry.yaml: %w", err)
258+
mergedRegistry := config.Registry{
259+
Servers: map[string]config.Tile{},
250260
}
251261

252-
cfg, err := config.ParseRegistryConfig(yaml)
253-
if err != nil {
254-
return config.Registry{}, fmt.Errorf("parsing registry.yaml: %w", err)
262+
for _, registryPath := range c.RegistryPath {
263+
if registryPath == "" {
264+
continue
265+
}
266+
267+
log(" - Reading registry from", registryPath)
268+
yaml, err := config.ReadConfigFile(ctx, c.docker, registryPath)
269+
if err != nil {
270+
return config.Registry{}, fmt.Errorf("reading registry file %s: %w", registryPath, err)
271+
}
272+
273+
cfg, err := config.ParseRegistryConfig(yaml)
274+
if err != nil {
275+
return config.Registry{}, fmt.Errorf("parsing registry file %s: %w", registryPath, err)
276+
}
277+
278+
// Merge servers into the combined registry, checking for overlaps
279+
for serverName, tile := range cfg.Servers {
280+
if _, exists := mergedRegistry.Servers[serverName]; exists {
281+
log(fmt.Sprintf("Warning: overlapping server '%s' found in registry '%s', overwriting previous value", serverName, registryPath))
282+
}
283+
mergedRegistry.Servers[serverName] = tile
284+
}
255285
}
256286

257-
return cfg, nil
287+
return mergedRegistry, nil
258288
}
259289

260290
func (c *FileBasedConfiguration) readConfig(ctx context.Context) (map[string]map[string]any, error) {
261-
if c.ConfigPath == "" {
291+
if len(c.ConfigPath) == 0 {
262292
return map[string]map[string]any{}, nil
263293
}
264294

265-
log(" - Reading config from", c.ConfigPath)
266-
yaml, err := config.ReadConfigFile(ctx, c.docker, c.ConfigPath)
267-
if err != nil {
268-
return nil, fmt.Errorf("reading config.yaml: %w", err)
269-
}
295+
mergedConfig := map[string]map[string]any{}
270296

271-
cfg, err := config.ParseConfig(yaml)
272-
if err != nil {
273-
return nil, fmt.Errorf("parsing config.yaml: %w", err)
297+
for _, configPath := range c.ConfigPath {
298+
if configPath == "" {
299+
continue
300+
}
301+
302+
log(" - Reading config from", configPath)
303+
yaml, err := config.ReadConfigFile(ctx, c.docker, configPath)
304+
if err != nil {
305+
return nil, fmt.Errorf("reading config file %s: %w", configPath, err)
306+
}
307+
308+
cfg, err := config.ParseConfig(yaml)
309+
if err != nil {
310+
return nil, fmt.Errorf("parsing config file %s: %w", configPath, err)
311+
}
312+
313+
// Merge configs into the combined config, checking for overlaps
314+
for serverName, serverConfig := range cfg {
315+
if _, exists := mergedConfig[serverName]; exists {
316+
log(fmt.Sprintf("Warning: overlapping server config '%s' found in config file '%s', overwriting previous value", serverName, configPath))
317+
}
318+
mergedConfig[serverName] = serverConfig
319+
}
274320
}
275321

276-
return cfg, nil
322+
return mergedConfig, nil
277323
}
278324

279325
func (c *FileBasedConfiguration) readDockerDesktopSecrets(ctx context.Context, servers map[string]catalog.Server, serverNames []string) (map[string]string, error) {

0 commit comments

Comments
 (0)