Skip to content

Commit e574a79

Browse files
Merge branch 'main' into bug/docker-compose--links-deprecated-#107
2 parents 1396e6b + 686fbe1 commit e574a79

File tree

13 files changed

+178
-117
lines changed

13 files changed

+178
-117
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"program": "${workspaceFolder}/tools/publisher/main.go",
2222
"args": [
2323
"-registry-url=http://localhost:8080",
24-
"-mcp-file=${workspaceFolder}/tools/publisher/mcp.json",
24+
"-mcp-file=${workspaceFolder}/tools/publisher/server.json",
2525
],
2626
}
2727
]

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ The service can be configured using environment variables:
289289
| Variable | Description | Default |
290290
|----------|-------------|---------|
291291
| `MCP_REGISTRY_APP_VERSION` | Application version | `dev` |
292+
| `MCP_REGISTRY_DATABASE_TYPE` | Database type | `mongodb` |
292293
| `MCP_REGISTRY_COLLECTION_NAME` | MongoDB collection name | `servers_v2` |
293294
| `MCP_REGISTRY_DATABASE_NAME` | MongoDB database name | `mcp-registry` |
294295
| `MCP_REGISTRY_DATABASE_URL` | MongoDB connection string | `mongodb://localhost:27017` |

cmd/registry/main.go

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/modelcontextprotocol/registry/internal/auth"
1616
"github.com/modelcontextprotocol/registry/internal/config"
1717
"github.com/modelcontextprotocol/registry/internal/database"
18+
"github.com/modelcontextprotocol/registry/internal/model"
1819
"github.com/modelcontextprotocol/registry/internal/service"
1920
)
2021

@@ -33,44 +34,62 @@ func main() {
3334

3435
log.Printf("Starting MCP Registry Application v%s (commit: %s)", Version, GitCommit)
3536

37+
var (
38+
registryService service.RegistryService
39+
db database.Database
40+
err error
41+
)
42+
3643
// Initialize configuration
3744
cfg := config.NewConfig()
3845

3946
// Initialize services based on environment
40-
var registryService service.RegistryService
41-
42-
// Use MongoDB for real registry service in production/other environments
43-
// Create a context with timeout for MongoDB connection
44-
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
45-
defer cancel()
47+
switch cfg.DatabaseType {
48+
case config.DatabaseTypeMemory:
49+
db = database.NewMemoryDB(map[string]*model.Server{})
50+
registryService = service.NewRegistryServiceWithDB(db)
51+
case config.DatabaseTypeMongoDB:
52+
// Use MongoDB for real registry service in production/other environments
53+
// Create a context with timeout for MongoDB connection
54+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
55+
defer cancel()
56+
57+
// Connect to MongoDB
58+
db, err = database.NewMongoDB(ctx, cfg.DatabaseURL, cfg.DatabaseName, cfg.CollectionName)
59+
if err != nil {
60+
log.Printf("Failed to connect to MongoDB: %v", err)
61+
return
62+
}
4663

47-
// Connect to MongoDB
48-
mongoDB, err := database.NewMongoDB(ctx, cfg.DatabaseURL, cfg.DatabaseName, cfg.CollectionName)
49-
if err != nil {
50-
log.Printf("Failed to connect to MongoDB: %v", err)
64+
// Create registry service with MongoDB
65+
registryService = service.NewRegistryServiceWithDB(db)
66+
log.Printf("MongoDB database name: %s", cfg.DatabaseName)
67+
log.Printf("MongoDB collection name: %s", cfg.CollectionName)
68+
69+
// Store the MongoDB instance for later cleanup
70+
defer func() {
71+
if err := db.Close(); err != nil {
72+
log.Printf("Error closing MongoDB connection: %v", err)
73+
} else {
74+
log.Println("MongoDB connection closed successfully")
75+
}
76+
}()
77+
default:
78+
log.Printf("Invalid database type: %s; supported types: %s, %s", cfg.DatabaseType, config.DatabaseTypeMemory, config.DatabaseTypeMongoDB)
5179
return
5280
}
5381

54-
// Create registry service with MongoDB
55-
registryService = service.NewRegistryServiceWithDB(mongoDB)
56-
log.Printf("MongoDB database name: %s", cfg.DatabaseName)
57-
log.Printf("MongoDB collection name: %s", cfg.CollectionName)
58-
59-
// Store the MongoDB instance for later cleanup
60-
defer func() {
61-
if err := mongoDB.Close(); err != nil {
62-
log.Printf("Error closing MongoDB connection: %v", err)
63-
} else {
64-
log.Println("MongoDB connection closed successfully")
65-
}
66-
}()
67-
82+
// Import seed data if requested (works for both memory and MongoDB)
6883
if cfg.SeedImport {
6984
log.Println("Importing data...")
70-
if err := database.ImportSeedFile(mongoDB, cfg.SeedFilePath); err != nil {
85+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
86+
defer cancel()
87+
88+
if err := db.ImportSeed(ctx, cfg.SeedFilePath); err != nil {
7189
log.Printf("Failed to import seed file: %v", err)
90+
} else {
91+
log.Println("Data import completed successfully")
7292
}
73-
log.Println("Data import completed successfully")
7493
}
7594

7695
// Initialize authentication services

docker-compose.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ services:
55
depends_on:
66
- mongodb
77
environment:
8-
- MCP_REGISTRY_DATABASE_URL=mongodb://mongodb:27017
9-
- MCP_REGISTRY_ENVIRONMENT=test
10-
- MCP_REGISTRY_GITHUB_CLIENT_ID
11-
- MCP_REGISTRY_GITHUB_CLIENT_SECRET
8+
- MCP_REGISTRY_DATABASE_URL=${MCP_REGISTRY_DATABASE_URL:-mongodb://mongodb:27017}
9+
- MCP_REGISTRY_ENVIRONMENT=${MCP_REGISTRY_ENVIRONMENT:-test}
10+
- MCP_REGISTRY_GITHUB_CLIENT_ID=${MCP_REGISTRY_GITHUB_CLIENT_ID}
11+
- MCP_REGISTRY_GITHUB_CLIENT_SECRET=${MCP_REGISTRY_GITHUB_CLIENT_SECRET}
1212
ports:
1313
- 8080:8080
1414
restart: "unless-stopped"

docs/openapi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ components:
108108
- id
109109
- name
110110
- description
111-
- repository
112111
- version_detail
113112
properties:
114113
id:

internal/config/config.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ import (
44
env "github.com/caarlos0/env/v11"
55
)
66

7+
type DatabaseType string
8+
9+
const (
10+
DatabaseTypeMongoDB DatabaseType = "mongodb"
11+
DatabaseTypeMemory DatabaseType = "memory"
12+
)
13+
714
// Config holds the application configuration
815
type Config struct {
9-
ServerAddress string `env:"SERVER_ADDRESS" envDefault:":8080"`
10-
DatabaseURL string `env:"DATABASE_URL" envDefault:"mongodb://localhost:27017"`
11-
DatabaseName string `env:"DATABASE_NAME" envDefault:"mcp-registry"`
12-
CollectionName string `env:"COLLECTION_NAME" envDefault:"servers_v2"`
13-
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
14-
SeedFilePath string `env:"SEED_FILE_PATH" envDefault:"data/seed.json"`
15-
SeedImport bool `env:"SEED_IMPORT" envDefault:"true"`
16-
Version string `env:"VERSION" envDefault:"dev"`
17-
GithubClientID string `env:"GITHUB_CLIENT_ID" envDefault:""`
18-
GithubClientSecret string `env:"GITHUB_CLIENT_SECRET" envDefault:""`
16+
ServerAddress string `env:"SERVER_ADDRESS" envDefault:":8080"`
17+
DatabaseType DatabaseType `env:"DATABASE_TYPE" envDefault:"mongodb"`
18+
DatabaseURL string `env:"DATABASE_URL" envDefault:"mongodb://localhost:27017"`
19+
DatabaseName string `env:"DATABASE_NAME" envDefault:"mcp-registry"`
20+
CollectionName string `env:"COLLECTION_NAME" envDefault:"servers_v2"`
21+
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
22+
SeedFilePath string `env:"SEED_FILE_PATH" envDefault:"data/seed.json"`
23+
SeedImport bool `env:"SEED_IMPORT" envDefault:"true"`
24+
Version string `env:"VERSION" envDefault:"dev"`
25+
GithubClientID string `env:"GITHUB_CLIENT_ID" envDefault:""`
26+
GithubClientSecret string `env:"GITHUB_CLIENT_SECRET" envDefault:""`
1927
}
2028

2129
// NewConfig creates a new configuration with default values

internal/database/database.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type Database interface {
2424
GetByID(ctx context.Context, id string) (*model.ServerDetail, error)
2525
// Publish adds a new ServerDetail to the database
2626
Publish(ctx context.Context, serverDetail *model.ServerDetail) error
27+
// ImportSeed imports initial data from a seed file
28+
ImportSeed(ctx context.Context, seedFilePath string) error
2729
// Close closes the database connection
2830
Close() error
2931
}

internal/database/import.go

Lines changed: 8 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,28 @@
11
package database
22

33
import (
4-
"context"
54
"encoding/json"
65
"fmt"
76
"log"
87
"os"
98
"path/filepath"
10-
"time"
119

1210
"github.com/modelcontextprotocol/registry/internal/model"
13-
"go.mongodb.org/mongo-driver/bson"
14-
"go.mongodb.org/mongo-driver/mongo"
15-
"go.mongodb.org/mongo-driver/mongo/options"
1611
)
1712

18-
// ImportSeedFile populates the MongoDB database with initial data from a seed file.
19-
func ImportSeedFile(mongo *MongoDB, seedFilePath string) error {
13+
// ReadSeedFile reads and parses the seed.json file - exported for use by all database implementations
14+
func ReadSeedFile(path string) ([]model.ServerDetail, error) {
15+
log.Printf("Reading seed file from %s", path)
16+
2017
// Set default seed file path if not provided
21-
if seedFilePath == "" {
18+
if path == "" {
2219
// Try to find the seed.json in the data directory
23-
seedFilePath = filepath.Join("data", "seed.json")
24-
if _, err := os.Stat(seedFilePath); os.IsNotExist(err) {
25-
return fmt.Errorf("seed file not found at %s", seedFilePath)
20+
path = filepath.Join("data", "seed.json")
21+
if _, err := os.Stat(path); os.IsNotExist(err) {
22+
return nil, fmt.Errorf("seed file not found at %s", path)
2623
}
2724
}
2825

29-
// Create a context with timeout for the database operations
30-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
31-
defer cancel()
32-
33-
// Read the seed file
34-
seedData, err := readSeedFile(seedFilePath)
35-
if err != nil {
36-
return fmt.Errorf("failed to read seed file: %w", err)
37-
}
38-
39-
collection := mongo.collection
40-
importData(ctx, collection, seedData)
41-
return nil
42-
}
43-
44-
// readSeedFile reads and parses the seed.json file
45-
func readSeedFile(path string) ([]model.ServerDetail, error) {
46-
log.Printf("Reading seed file from %s", path)
47-
4826
// Read the file content
4927
fileContent, err := os.ReadFile(path)
5028
if err != nil {
@@ -64,44 +42,3 @@ func readSeedFile(path string) ([]model.ServerDetail, error) {
6442
log.Printf("Found %d server entries in seed file", len(servers))
6543
return servers, nil
6644
}
67-
68-
// importData imports the seed data into MongoDB
69-
func importData(ctx context.Context, collection *mongo.Collection, servers []model.ServerDetail) {
70-
log.Printf("Importing %d servers into collection %s", len(servers), collection.Name())
71-
72-
for i, server := range servers {
73-
if server.ID == "" || server.Name == "" {
74-
log.Printf("Skipping server %d: ID or Name is empty", i+1)
75-
continue
76-
}
77-
// Create filter based on server ID
78-
filter := bson.M{"id": server.ID}
79-
80-
if server.VersionDetail.Version == "" {
81-
server.VersionDetail.Version = "0.0.1-seed"
82-
server.VersionDetail.ReleaseDate = time.Now().Format(time.RFC3339)
83-
server.VersionDetail.IsLatest = true
84-
}
85-
// Create update document
86-
update := bson.M{"$set": server}
87-
88-
// Use upsert to create if not exists or update if exists
89-
opts := options.Update().SetUpsert(true)
90-
result, err := collection.UpdateOne(ctx, filter, update, opts)
91-
if err != nil {
92-
log.Printf("Error importing server %s: %v", server.ID, err)
93-
continue
94-
}
95-
96-
switch {
97-
case result.UpsertedCount > 0:
98-
log.Printf("[%d/%d] Created server: %s", i+1, len(servers), server.Name)
99-
case result.ModifiedCount > 0:
100-
log.Printf("[%d/%d] Updated server: %s", i+1, len(servers), server.Name)
101-
default:
102-
log.Printf("[%d/%d] Server already up to date: %s", i+1, len(servers), server.Name)
103-
}
104-
}
105-
106-
log.Println("Import completed successfully")
107-
}

internal/database/memory.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package database
22

33
import (
44
"context"
5+
"fmt"
6+
"log"
57
"sort"
68
"strconv"
79
"strings"
@@ -249,6 +251,47 @@ func (db *MemoryDB) Publish(ctx context.Context, serverDetail *model.ServerDetai
249251
return nil
250252
}
251253

254+
// ImportSeed imports initial data from a seed file into memory database
255+
func (db *MemoryDB) ImportSeed(ctx context.Context, seedFilePath string) error {
256+
if ctx.Err() != nil {
257+
return ctx.Err()
258+
}
259+
260+
// Read the seed file
261+
seedData, err := ReadSeedFile(seedFilePath)
262+
if err != nil {
263+
return fmt.Errorf("failed to read seed file: %w", err)
264+
}
265+
266+
log.Printf("Importing %d servers into memory database", len(seedData))
267+
268+
db.mu.Lock()
269+
defer db.mu.Unlock()
270+
271+
for i, server := range seedData {
272+
if server.ID == "" || server.Name == "" {
273+
log.Printf("Skipping server %d: ID or Name is empty", i+1)
274+
continue
275+
}
276+
277+
// Set default version information if missing
278+
if server.VersionDetail.Version == "" {
279+
server.VersionDetail.Version = "0.0.1-seed"
280+
server.VersionDetail.ReleaseDate = time.Now().Format(time.RFC3339)
281+
server.VersionDetail.IsLatest = true
282+
}
283+
284+
// Store a copy of the server detail
285+
serverDetailCopy := server
286+
db.entries[server.ID] = &serverDetailCopy
287+
288+
log.Printf("[%d/%d] Imported server: %s", i+1, len(seedData), server.Name)
289+
}
290+
291+
log.Println("Memory database import completed successfully")
292+
return nil
293+
}
294+
252295
// Close closes the database connection
253296
// For an in-memory database, this is a no-op
254297
func (db *MemoryDB) Close() error {

0 commit comments

Comments
 (0)