Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions cmd/registry/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,56 +34,64 @@ 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
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
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)

Expand Down
2 changes: 2 additions & 0 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
79 changes: 8 additions & 71 deletions internal/database/import.go
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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")
}
43 changes: 43 additions & 0 deletions internal/database/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package database

import (
"context"
"fmt"
"log"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -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 {
Expand Down
52 changes: 52 additions & 0 deletions internal/database/mongo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Loading