diff --git a/cmd/registry/main.go b/cmd/registry/main.go index cb776f56..387462da 100644 --- a/cmd/registry/main.go +++ b/cmd/registry/main.go @@ -34,16 +34,20 @@ func main() { log.Printf("Starting MCP Registry Application v%s (commit: %s)", Version, GitCommit) + var ( + registryService service.RegistryService + db database.Database + err error + ) + // Initialize configuration cfg := config.NewConfig() // Initialize services based on environment - var registryService service.RegistryService - switch cfg.DatabaseType { case config.DatabaseTypeMemory: - memoryDB := database.NewMemoryDB(map[string]*model.Server{}) - registryService = service.NewRegistryServiceWithDB(memoryDB) + db = database.NewMemoryDB(map[string]*model.Server{}) + registryService = service.NewRegistryServiceWithDB(db) case config.DatabaseTypeMongoDB: // Use MongoDB for real registry service in production/other environments // Create a context with timeout for MongoDB connection @@ -51,39 +55,43 @@ func main() { defer cancel() // Connect to MongoDB - mongoDB, err := database.NewMongoDB(ctx, cfg.DatabaseURL, cfg.DatabaseName, cfg.CollectionName) + db, err = database.NewMongoDB(ctx, cfg.DatabaseURL, cfg.DatabaseName, cfg.CollectionName) if err != nil { log.Printf("Failed to connect to MongoDB: %v", err) return } // Create registry service with MongoDB - registryService = service.NewRegistryServiceWithDB(mongoDB) + registryService = service.NewRegistryServiceWithDB(db) log.Printf("MongoDB database name: %s", cfg.DatabaseName) log.Printf("MongoDB collection name: %s", cfg.CollectionName) // Store the MongoDB instance for later cleanup defer func() { - if err := mongoDB.Close(); err != nil { + if err := db.Close(); err != nil { log.Printf("Error closing MongoDB connection: %v", err) } else { log.Println("MongoDB connection closed successfully") } }() - - if cfg.SeedImport { - log.Println("Importing data...") - if err := database.ImportSeedFile(mongoDB, cfg.SeedFilePath); err != nil { - log.Printf("Failed to import seed file: %v", err) - } else { - log.Println("Data import completed successfully") - } - } default: log.Printf("Invalid database type: %s; supported types: %s, %s", cfg.DatabaseType, config.DatabaseTypeMemory, config.DatabaseTypeMongoDB) return } + // Import seed data if requested (works for both memory and MongoDB) + if cfg.SeedImport { + log.Println("Importing data...") + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + if err := db.ImportSeed(ctx, cfg.SeedFilePath); err != nil { + log.Printf("Failed to import seed file: %v", err) + } else { + log.Println("Data import completed successfully") + } + } + // Initialize authentication services authService := auth.NewAuthService(cfg) diff --git a/internal/database/database.go b/internal/database/database.go index 1d5fc4f8..f2e8a0e2 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -24,6 +24,8 @@ type Database interface { GetByID(ctx context.Context, id string) (*model.ServerDetail, error) // Publish adds a new ServerDetail to the database Publish(ctx context.Context, serverDetail *model.ServerDetail) error + // ImportSeed imports initial data from a seed file + ImportSeed(ctx context.Context, seedFilePath string) error // Close closes the database connection Close() error } diff --git a/internal/database/import.go b/internal/database/import.go index ee70f107..9df209ce 100644 --- a/internal/database/import.go +++ b/internal/database/import.go @@ -1,50 +1,28 @@ package database import ( - "context" "encoding/json" "fmt" "log" "os" "path/filepath" - "time" "github.com/modelcontextprotocol/registry/internal/model" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" ) -// ImportSeedFile populates the MongoDB database with initial data from a seed file. -func ImportSeedFile(mongo *MongoDB, seedFilePath string) error { +// ReadSeedFile reads and parses the seed.json file - exported for use by all database implementations +func ReadSeedFile(path string) ([]model.ServerDetail, error) { + log.Printf("Reading seed file from %s", path) + // Set default seed file path if not provided - if seedFilePath == "" { + if path == "" { // Try to find the seed.json in the data directory - seedFilePath = filepath.Join("data", "seed.json") - if _, err := os.Stat(seedFilePath); os.IsNotExist(err) { - return fmt.Errorf("seed file not found at %s", seedFilePath) + path = filepath.Join("data", "seed.json") + if _, err := os.Stat(path); os.IsNotExist(err) { + return nil, fmt.Errorf("seed file not found at %s", path) } } - // Create a context with timeout for the database operations - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - defer cancel() - - // Read the seed file - seedData, err := readSeedFile(seedFilePath) - if err != nil { - return fmt.Errorf("failed to read seed file: %w", err) - } - - collection := mongo.collection - importData(ctx, collection, seedData) - return nil -} - -// readSeedFile reads and parses the seed.json file -func readSeedFile(path string) ([]model.ServerDetail, error) { - log.Printf("Reading seed file from %s", path) - // Read the file content fileContent, err := os.ReadFile(path) if err != nil { @@ -64,44 +42,3 @@ func readSeedFile(path string) ([]model.ServerDetail, error) { log.Printf("Found %d server entries in seed file", len(servers)) return servers, nil } - -// importData imports the seed data into MongoDB -func importData(ctx context.Context, collection *mongo.Collection, servers []model.ServerDetail) { - log.Printf("Importing %d servers into collection %s", len(servers), collection.Name()) - - for i, server := range servers { - if server.ID == "" || server.Name == "" { - log.Printf("Skipping server %d: ID or Name is empty", i+1) - continue - } - // Create filter based on server ID - filter := bson.M{"id": server.ID} - - if server.VersionDetail.Version == "" { - server.VersionDetail.Version = "0.0.1-seed" - server.VersionDetail.ReleaseDate = time.Now().Format(time.RFC3339) - server.VersionDetail.IsLatest = true - } - // Create update document - update := bson.M{"$set": server} - - // Use upsert to create if not exists or update if exists - opts := options.Update().SetUpsert(true) - result, err := collection.UpdateOne(ctx, filter, update, opts) - if err != nil { - log.Printf("Error importing server %s: %v", server.ID, err) - continue - } - - switch { - case result.UpsertedCount > 0: - log.Printf("[%d/%d] Created server: %s", i+1, len(servers), server.Name) - case result.ModifiedCount > 0: - log.Printf("[%d/%d] Updated server: %s", i+1, len(servers), server.Name) - default: - log.Printf("[%d/%d] Server already up to date: %s", i+1, len(servers), server.Name) - } - } - - log.Println("Import completed successfully") -} diff --git a/internal/database/memory.go b/internal/database/memory.go index 5494b99f..fd892a69 100644 --- a/internal/database/memory.go +++ b/internal/database/memory.go @@ -2,6 +2,8 @@ package database import ( "context" + "fmt" + "log" "sort" "strconv" "strings" @@ -249,6 +251,47 @@ func (db *MemoryDB) Publish(ctx context.Context, serverDetail *model.ServerDetai return nil } +// ImportSeed imports initial data from a seed file into memory database +func (db *MemoryDB) ImportSeed(ctx context.Context, seedFilePath string) error { + if ctx.Err() != nil { + return ctx.Err() + } + + // Read the seed file + seedData, err := ReadSeedFile(seedFilePath) + if err != nil { + return fmt.Errorf("failed to read seed file: %w", err) + } + + log.Printf("Importing %d servers into memory database", len(seedData)) + + db.mu.Lock() + defer db.mu.Unlock() + + for i, server := range seedData { + if server.ID == "" || server.Name == "" { + log.Printf("Skipping server %d: ID or Name is empty", i+1) + continue + } + + // Set default version information if missing + if server.VersionDetail.Version == "" { + server.VersionDetail.Version = "0.0.1-seed" + server.VersionDetail.ReleaseDate = time.Now().Format(time.RFC3339) + server.VersionDetail.IsLatest = true + } + + // Store a copy of the server detail + serverDetailCopy := server + db.entries[server.ID] = &serverDetailCopy + + log.Printf("[%d/%d] Imported server: %s", i+1, len(seedData), server.Name) + } + + log.Println("Memory database import completed successfully") + return nil +} + // Close closes the database connection // For an in-memory database, this is a no-op func (db *MemoryDB) Close() error { diff --git a/internal/database/mongo.go b/internal/database/mongo.go index a553d0fa..3dbf776e 100644 --- a/internal/database/mongo.go +++ b/internal/database/mongo.go @@ -232,6 +232,58 @@ func (db *MongoDB) Publish(ctx context.Context, serverDetail *model.ServerDetail return nil } +// ImportSeed imports initial data from a seed file into MongoDB +func (db *MongoDB) ImportSeed(ctx context.Context, seedFilePath string) error { + // Read the seed file + servers, err := ReadSeedFile(seedFilePath) + if err != nil { + return fmt.Errorf("failed to read seed file: %w", err) + } + + collection := db.collection + + log.Printf("Importing %d servers into collection %s", len(servers), collection.Name()) + + for i, server := range servers { + if server.ID == "" || server.Name == "" { + log.Printf("Skipping server %d: ID or Name is empty", i+1) + continue + } + + if server.VersionDetail.Version == "" { + server.VersionDetail.Version = "0.0.1-seed" + server.VersionDetail.ReleaseDate = time.Now().Format(time.RFC3339) + server.VersionDetail.IsLatest = true + } + + // Create filter based on server ID + filter := bson.M{"id": server.ID} + + // Create update document + update := bson.M{"$set": server} + + // Use upsert to create if not exists or update if exists + opts := options.Update().SetUpsert(true) + result, err := collection.UpdateOne(ctx, filter, update, opts) + if err != nil { + log.Printf("Error importing server %s: %v", server.ID, err) + continue + } + + switch { + case result.UpsertedCount > 0: + log.Printf("[%d/%d] Created server: %s", i+1, len(servers), server.Name) + case result.ModifiedCount > 0: + log.Printf("[%d/%d] Updated server: %s", i+1, len(servers), server.Name) + default: + log.Printf("[%d/%d] Server already up to date: %s", i+1, len(servers), server.Name) + } + } + + log.Println("MongoDB database import completed successfully") + return nil +} + // Close closes the database connection func (db *MongoDB) Close() error { return db.client.Disconnect(context.Background())