Skip to content

Commit 6903da8

Browse files
Merge pull request #131 from docker/slim/officialregistry
Updates for supporting OfficialRegistry
2 parents 247ce39 + 06c93da commit 6903da8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2095
-845
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
!/.git
1010
!/server.go
1111
!/test/servers
12+
!/test/testdata

cmd/docker-mcp/commands/catalog.go

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package commands
22

33
import (
4+
"context"
5+
"encoding/json"
46
"fmt"
57

68
"github.com/spf13/cobra"
79

810
"github.com/docker/mcp-gateway/cmd/docker-mcp/catalog"
11+
catalogTypes "github.com/docker/mcp-gateway/cmd/docker-mcp/internal/catalog"
12+
"github.com/docker/mcp-gateway/cmd/docker-mcp/internal/yq"
913
)
1014

1115
func catalogCommand() *cobra.Command {
@@ -31,21 +35,40 @@ func catalogCommand() *cobra.Command {
3135
}
3236

3337
func importCatalogCommand() *cobra.Command {
34-
return &cobra.Command{
38+
var mcpRegistry string
39+
var dryRun bool
40+
cmd := &cobra.Command{
3541
Use: "import <alias|url|file>",
3642
Short: "Import a catalog from URL or file",
3743
Long: `Import an MCP server catalog from a URL or local file. The catalog will be downloaded
38-
and stored locally for use with the MCP gateway.`,
44+
and stored locally for use with the MCP gateway.
45+
46+
When --mcp-registry flag is used, the argument must be an existing catalog name, and the
47+
command will import servers from the MCP registry URL into that catalog.`,
3948
Args: cobra.ExactArgs(1),
4049
Example: ` # Import from URL
4150
docker mcp catalog import https://example.com/my-catalog.yaml
4251
4352
# Import from local file
44-
docker mcp catalog import ./shared-catalog.yaml`,
53+
docker mcp catalog import ./shared-catalog.yaml
54+
55+
# Import from MCP registry URL into existing catalog
56+
docker mcp catalog import my-catalog --mcp-registry https://registry.example.com/server`,
4557
RunE: func(cmd *cobra.Command, args []string) error {
58+
// If mcp-registry flag is provided, import to existing catalog
59+
if mcpRegistry != "" {
60+
if dryRun {
61+
return runOfficialregistryImport(cmd.Context(), mcpRegistry, nil)
62+
}
63+
return importMCPRegistryToCatalog(cmd.Context(), args[0], mcpRegistry)
64+
}
65+
// Default behavior: import entire catalog
4666
return catalog.Import(cmd.Context(), args[0])
4767
},
4868
}
69+
cmd.Flags().StringVar(&mcpRegistry, "mcp-registry", "", "Import server from MCP registry URL into existing catalog")
70+
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Show Imported Data but do not update the Catalog")
71+
return cmd
4972
}
5073

5174
func exportCatalogCommand() *cobra.Command {
@@ -237,3 +260,69 @@ func resetCatalogCommand() *cobra.Command {
237260
},
238261
}
239262
}
263+
264+
// importMCPRegistryToCatalog imports a server from an MCP registry URL into an existing catalog
265+
func importMCPRegistryToCatalog(ctx context.Context, catalogName, mcpRegistryURL string) error {
266+
// Check if the catalog exists
267+
cfg, err := catalog.ReadConfig()
268+
if err != nil {
269+
return fmt.Errorf("failed to read catalog config: %w", err)
270+
}
271+
272+
_, exists := cfg.Catalogs[catalogName]
273+
if !exists {
274+
return fmt.Errorf("catalog '%s' does not exist", catalogName)
275+
}
276+
277+
// Prevent users from modifying the Docker catalog
278+
if catalogName == catalog.DockerCatalogName {
279+
return fmt.Errorf("cannot import servers into catalog '%s' as it is managed by Docker", catalogName)
280+
}
281+
282+
// Fetch server from MCP registry
283+
var servers []catalogTypes.Server
284+
if err := runOfficialregistryImport(ctx, mcpRegistryURL, &servers); err != nil {
285+
return fmt.Errorf("failed to fetch server from MCP registry: %w", err)
286+
}
287+
288+
if len(servers) == 0 {
289+
return fmt.Errorf("no servers found at MCP registry URL")
290+
}
291+
292+
// For now, we'll import the first server (MCP registry URLs typically contain one server)
293+
server := servers[0]
294+
295+
serverName := server.Name
296+
297+
// Convert the server to JSON for injection into the catalog
298+
serverJSON, err := json.Marshal(server)
299+
if err != nil {
300+
return fmt.Errorf("failed to marshal server: %w", err)
301+
}
302+
303+
// Read the current catalog content
304+
catalogContent, err := catalog.ReadCatalogFile(catalogName)
305+
if err != nil {
306+
return fmt.Errorf("failed to read catalog file: %w", err)
307+
}
308+
309+
// Inject the server into the catalog using the same pattern as the add function
310+
updatedContent, err := injectServerIntoCatalog(catalogContent, serverName, serverJSON)
311+
if err != nil {
312+
return fmt.Errorf("failed to inject server into catalog: %w", err)
313+
}
314+
315+
// Write the updated catalog back
316+
if err := catalog.WriteCatalogFile(catalogName, updatedContent); err != nil {
317+
return fmt.Errorf("failed to write updated catalog: %w", err)
318+
}
319+
320+
fmt.Printf("Successfully imported server '%s' from MCP registry into catalog '%s'\n", serverName, catalogName)
321+
return nil
322+
}
323+
324+
// injectServerIntoCatalog injects a server JSON into a catalog YAML using yq
325+
func injectServerIntoCatalog(yamlData []byte, serverName string, serverJSON []byte) ([]byte, error) {
326+
query := fmt.Sprintf(`.registry."%s" = %s`, serverName, string(serverJSON))
327+
return yq.Evaluate(query, yamlData, yq.NewYamlDecoder(), yq.NewYamlEncoder())
328+
}

cmd/docker-mcp/commands/feature.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ func featureEnableCommand(dockerCli command.Cli) *cobra.Command {
3737
Long: `Enable an experimental feature.
3838
3939
Available features:
40-
configured-catalogs Allow gateway to use user-managed catalogs alongside Docker catalog
4140
oauth-interceptor Enable GitHub OAuth flow interception for automatic authentication
4241
dynamic-tools Enable internal MCP management tools (mcp-find, mcp-add, mcp-remove)`,
4342
Args: cobra.ExactArgs(1),
@@ -46,7 +45,7 @@ Available features:
4645

4746
// Validate feature name
4847
if !isKnownFeature(featureName) {
49-
return fmt.Errorf("unknown feature: %s\n\nAvailable features:\n configured-catalogs Allow gateway to use user-managed catalogs\n oauth-interceptor Enable GitHub OAuth flow interception\n dynamic-tools Enable internal MCP management tools", featureName)
48+
return fmt.Errorf("unknown feature: %s\n\nAvailable features:\n oauth-interceptor Enable GitHub OAuth flow interception\n dynamic-tools Enable internal MCP management tools", featureName)
5049
}
5150

5251
// Enable the feature
@@ -65,12 +64,6 @@ Available features:
6564

6665
// Provide usage hints for features
6766
switch featureName {
68-
case "configured-catalogs":
69-
fmt.Println("\nTo use configured catalogs with the gateway, run:")
70-
fmt.Println(" docker mcp gateway run --use-configured-catalogs")
71-
fmt.Println("\nTo create and manage catalogs, use:")
72-
fmt.Println(" docker mcp catalog create <name>")
73-
fmt.Println(" docker mcp catalog add <catalog> <server-name> <server-file>")
7467
case "oauth-interceptor":
7568
fmt.Println("\nThis feature enables automatic GitHub OAuth interception when 401 errors occur.")
7669
fmt.Println("When enabled, the gateway will automatically provide OAuth URLs for authentication.")
@@ -135,7 +128,7 @@ func featureListCommand(dockerCli command.Cli) *cobra.Command {
135128
fmt.Println()
136129

137130
// Show all known features
138-
knownFeatures := []string{"configured-catalogs", "oauth-interceptor", "dynamic-tools"}
131+
knownFeatures := []string{"oauth-interceptor", "dynamic-tools"}
139132
for _, feature := range knownFeatures {
140133
status := "disabled"
141134
if isFeatureEnabledFromCli(dockerCli, feature) {
@@ -146,8 +139,6 @@ func featureListCommand(dockerCli command.Cli) *cobra.Command {
146139

147140
// Add description for each feature
148141
switch feature {
149-
case "configured-catalogs":
150-
fmt.Printf(" %-20s %s\n", "", "Allow gateway to use user-managed catalogs alongside Docker catalog")
151142
case "oauth-interceptor":
152143
fmt.Printf(" %-20s %s\n", "", "Enable GitHub OAuth flow interception for automatic authentication")
153144
case "dynamic-tools":
@@ -212,7 +203,6 @@ func isFeatureEnabledFromConfig(configFile *configfile.ConfigFile, feature strin
212203
// isKnownFeature checks if the feature name is valid
213204
func isKnownFeature(feature string) bool {
214205
knownFeatures := []string{
215-
"configured-catalogs",
216206
"oauth-interceptor",
217207
"dynamic-tools",
218208
}

0 commit comments

Comments
 (0)