Skip to content

Commit ebaf4c9

Browse files
authored
Refactor database implementation to use PostgreSQL instead of MongoDB (#289)
Fixes #228 ## Motivation and Context See #19 ## How Has This Been Tested? - Run locally - Run in docker compose - Run infra in minikube - Unit and integration tests all passing :) ## Breaking Changes Yes: users will not be able to access any data they used to have in custom mongodb. We are accepting this breaking change, given the registry is in experimental development status. ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [x] New and existing tests pass locally - [x] I have added appropriate error handling - [x] I have added or updated documentation as needed ## Additional context I have not implemented connection pooling support for simplicity, and because cloudnative-pg already provides built-in connection pooling with pgBouncer so if we want it later we can switch to this endpoint easily.
1 parent ee910ca commit ebaf4c9

23 files changed

+846
-564
lines changed

.env.example

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ MCP_REGISTRY_VERSION=dev
66
MCP_REGISTRY_LOG_LEVEL=info
77

88
# Database configuration
9-
MCP_REGISTRY_DATABASE_TYPE=mongodb
10-
MCP_REGISTRY_DATABASE_URL=mongodb://username:password@localhost:27017
9+
# Supported types: postgresql, memory
10+
MCP_REGISTRY_DATABASE_TYPE=postgresql
11+
MCP_REGISTRY_DATABASE_URL=postgres://username:password@localhost:5432/mcp-registry
1112
MCP_REGISTRY_DATABASE_NAME=mcp-registry
1213
MCP_REGISTRY_COLLECTION_NAME=servers_v2
1314

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ check: lint validate test-all ## Run all checks (lint, validate, unit tests)
5757
dev-compose: ## Start development environment with Docker Compose (builds image automatically)
5858
docker compose up --build
5959

60-
dev-local: ## Run registry locally (requires MongoDB)
60+
dev-local: ## Run registry locally
6161
go run ./cmd/registry
6262

6363
# Cleanup

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The MCP Registry service provides a centralized repository for MCP server entrie
2424
- Health check endpoint for service monitoring
2525
- Support for various environment configurations
2626
- Graceful shutdown handling
27-
- MongoDB and in-memory database support
27+
- PostgreSQL and in-memory database support
2828
- Comprehensive API documentation
2929
- Pagination support for listing registry entries
3030
- Seed data export/import composability with HTTP support
@@ -35,7 +35,7 @@ The MCP Registry service provides a centralized repository for MCP server entrie
3535
### Prerequisites
3636

3737
- Go 1.24.x (required - check with `go version`)
38-
- MongoDB
38+
- PostgreSQL
3939
- Docker (optional, but recommended for development)
4040

4141
For development:
@@ -46,14 +46,13 @@ For development:
4646

4747
## Running
4848

49-
The easiest way to get the registry running is uses docker compose. This will setup the MCP Registry service, import the seed data and run MongoDB in a local Docker environment.
49+
The easiest way to get the registry running is uses docker compose. This will setup the MCP Registry service, import the seed data and run PostgreSQL in a local Docker environment.
5050

5151
```bash
52-
# Run the registry and MongoDB with docker compose
5352
make dev-compose
5453
```
5554

56-
This will start the MCP Registry service and MongoDB with Docker, running at [`localhost:8080`](http://localhost:8080).
55+
This will start the MCP Registry service running at [`localhost:8080`](http://localhost:8080).
5756

5857
## Building
5958

@@ -63,15 +62,15 @@ If you prefer to run the service locally without Docker, you can build and run i
6362
# Build a registry executable
6463
make build
6564
```
66-
This will create the `registry` binary in the current directory. You'll need to have MongoDB running locally or with Docker.
65+
This will create the `registry` binary in the current directory.
6766

6867
To run the service locally:
6968
```bash
70-
# Run registry locally (requires MongoDB)
69+
# Run registry locally
7170
make dev-local
7271
```
7372

74-
By default, the service will run on [`localhost:8080`](http://localhost:8080).
73+
By default, the service will run on [`localhost:8080`](http://localhost:8080). You'll need to use the in-memory database or have PostgreSQL running.
7574

7675
To build the CLI tool for publishing MCP servers to the registry:
7776

@@ -95,11 +94,12 @@ Key development commands:
9594
```bash
9695
# Development
9796
make dev-compose # Start development environment with Docker Compose
98-
make dev-local # Run registry locally (requires MongoDB)
97+
make dev-local # Run registry locally
9998

10099
# Build targets
101100
make build # Build the registry application
102101
make publisher # Build the publisher tool
102+
make migrate # Build the database migration tool
103103

104104
# Testing
105105
make test-unit # Run unit tests with coverage report
@@ -156,7 +156,7 @@ This will prevent commits that fail linting or have formatting issues.
156156
│ ├── api/ # HTTP server and request handlers (routing)
157157
│ ├── auth/ # GitHub OAuth integration
158158
│ ├── config/ # Configuration management
159-
│ ├── database/ # Data persistence abstraction (MongoDB and in-memory)
159+
│ ├── database/ # Data persistence abstraction
160160
│ ├── model/ # Data models and domain structures
161161
│ └── service/ # Business logic implementation
162162
├── pkg/ # Public libraries
@@ -175,9 +175,9 @@ This will prevent commits that fail linting or have formatting issues.
175175
5. JSON responses returned to clients
176176

177177
### Key Interfaces
178-
- **Database Interface** (`internal/database/database.go`) - Abstracts data persistence with MongoDB and memory implementations
178+
- **Database Interface** (`internal/database/database.go`) - Abstracts data persistence with PostgreSQL and in-memory implementations
179179
- **RegistryService** (`internal/service/service.go`) - Business logic abstraction over database
180-
- **Auth Service** (`internal/auth/auth.go`) - GitHub OAuth token validation
180+
- **Auth Service** (`internal/auth/jwt.go`) - Registry token creation and validation
181181

182182
### Authentication Flow
183183
Publishing requires GitHub OAuth validation:

cmd/registry/main.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,38 +48,37 @@ func main() {
4848
case config.DatabaseTypeMemory:
4949
db = database.NewMemoryDB(map[string]*model.Server{})
5050
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
51+
case config.DatabaseTypePostgreSQL:
52+
// Use PostgreSQL for real registry service
53+
// Create a context with timeout for PostgreSQL connection
5454
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
5555
defer cancel()
5656

57-
// Connect to MongoDB
58-
db, err = database.NewMongoDB(ctx, cfg.DatabaseURL, cfg.DatabaseName, cfg.CollectionName)
57+
// Connect to PostgreSQL
58+
db, err = database.NewPostgreSQL(ctx, cfg.DatabaseURL)
5959
if err != nil {
60-
log.Printf("Failed to connect to MongoDB: %v", err)
60+
log.Printf("Failed to connect to PostgreSQL: %v", err)
6161
return
6262
}
6363

64-
// Create registry service with MongoDB
64+
// Create registry service with PostgreSQL
6565
registryService = service.NewRegistryServiceWithDB(db)
66-
log.Printf("MongoDB database name: %s", cfg.DatabaseName)
67-
log.Printf("MongoDB collection name: %s", cfg.CollectionName)
66+
log.Printf("PostgreSQL database URL: %s", cfg.DatabaseURL)
6867

69-
// Store the MongoDB instance for later cleanup
68+
// Store the PostgreSQL instance for later cleanup
7069
defer func() {
7170
if err := db.Close(); err != nil {
72-
log.Printf("Error closing MongoDB connection: %v", err)
71+
log.Printf("Error closing PostgreSQL connection: %v", err)
7372
} else {
74-
log.Println("MongoDB connection closed successfully")
73+
log.Println("PostgreSQL connection closed successfully")
7574
}
7675
}()
7776
default:
78-
log.Printf("Invalid database type: %s; supported types: %s, %s", cfg.DatabaseType, config.DatabaseTypeMemory, config.DatabaseTypeMongoDB)
77+
log.Printf("Invalid database type: %s; supported types: %s, %s", cfg.DatabaseType, config.DatabaseTypeMemory, config.DatabaseTypePostgreSQL)
7978
return
8079
}
8180

82-
// Import seed data if seed source is provided (works for both memory and MongoDB)
81+
// Import seed data if seed source is provided
8382
if cfg.SeedFrom != "" {
8483
log.Printf("Importing data from %s...", cfg.SeedFrom)
8584
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)

deploy/Pulumi.local.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ config:
33
mcp-registry:provider: local
44
mcp-registry:githubClientId: Iv23licy3GSiM9Km5jtd
55
mcp-registry:githubClientSecret: 0e8db54879b02c29adef51795586f3c510a9341d
6+
mcp-registry:jwtPrivateKey: bb2c6b424005acd5df47a9e2c87f446def86dd740c888ea3efb825b23f7ef47c
67
encryptionsalt: v1:ijIHaqhbXVA=:v1:7voX1Kv+Bunz33iN:fyVHMOhlGIymzJ+ILgUBy3ExTwUUnA==

deploy/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Pre-requisites:
1111
- Access to a Kubernetes cluster via kubeconfig. You can run a cluster locally with [minikube](https://minikube.sigs.k8s.io/docs/start/).
1212

1313
1. Ensure your kubeconfig is configured at the cluster you want to use. For minikube, run `minikube start && minikube tunnel`.
14-
2. Run `make local-up` to deploy the stack. Run this again if the first attempt fails.
14+
2. Run `make local-up` to deploy the stack.
1515
3. Access the repository via the ingress load balancer. You can find its external IP with `kubectl get svc ingress-nginx-controller -n ingress-nginx`. Then run `curl -H "Host: local.registry.modelcontextprotocol.io" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.
1616

1717
#### To change config
@@ -103,7 +103,7 @@ Pre-requisites:
103103
│ ├── cert_manager.go # SSL certificate management
104104
│ ├── deploy.go # Deployment orchestration
105105
│ ├── ingress.go # Ingress controller setup
106-
│ ├── mongodb.go # MongoDB deployment
106+
│ ├── postgres.go # PostgreSQL database deployment
107107
│ └── registry.go # MCP Registry deployment
108108
└── providers/ # Kubernetes cluster providers
109109
├── types.go # Provider interface definitions
@@ -122,7 +122,7 @@ Pre-requisites:
122122
5. `k8s.DeployAll()` orchestrates complete deployment:
123123
- Certificate manager for SSL/TLS
124124
- Ingress controller for external access
125-
- MongoDB for data persistence
125+
- Database for data persistence
126126
- MCP Registry application
127127
128128
## Configuration
@@ -152,5 +152,5 @@ kubectl get svc -n ingress-nginx
152152
153153
```bash
154154
kubectl logs -l app=mcp-registry
155-
kubectl logs -l app=mongodb
155+
kubectl logs -l app=postgres
156156
```

deploy/pkg/k8s/cert_manager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func SetupCertManager(ctx *pulumi.Context, cluster *providers.ProviderInfo) erro
6969
},
7070
},
7171
},
72-
}, pulumi.Provider(cluster.Provider), pulumi.DependsOn([]pulumi.Resource{certManager}))
72+
}, pulumi.Provider(cluster.Provider), pulumi.DependsOnInputs(certManager.Ready))
7373
if err != nil {
7474
return err
7575
}

deploy/pkg/k8s/deploy.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ func DeployAll(ctx *pulumi.Context, cluster *providers.ProviderInfo, environment
1616
}
1717

1818
// Setup ingress controller
19-
err = SetupIngressController(ctx, cluster, environment)
19+
ingressNginx, err := SetupIngressController(ctx, cluster, environment)
2020
if err != nil {
2121
return nil, err
2222
}
2323

24-
// Deploy MongoDB
25-
err = DeployMongoDB(ctx, cluster, environment)
24+
// Deploy PostgreSQL databases
25+
pgCluster, err := DeployPostgresDatabases(ctx, cluster, environment)
2626
if err != nil {
2727
return nil, err
2828
}
2929

3030
// Deploy MCP Registry
31-
service, err = DeployMCPRegistry(ctx, cluster, environment)
31+
service, err = DeployMCPRegistry(ctx, cluster, environment, ingressNginx, pgCluster)
3232
if err != nil {
3333
return nil, err
3434
}

deploy/pkg/k8s/ingress.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
// SetupIngressController sets up the NGINX Ingress Controller
16-
func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo, environment string) error {
16+
func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo, environment string) (*helm.Chart, error) {
1717
conf := config.New(ctx, "mcp-registry")
1818
provider := conf.Get("provider")
1919
if provider == "" {
@@ -27,7 +27,7 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
2727
},
2828
}, pulumi.Provider(cluster.Provider))
2929
if err != nil {
30-
return err
30+
return nil, err
3131
}
3232

3333
// Install NGINX Ingress Controller
@@ -60,7 +60,7 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
6060
},
6161
}, pulumi.Provider(cluster.Provider))
6262
if err != nil {
63-
return err
63+
return nil, err
6464
}
6565

6666
// Extract ingress IPs from the Helm chart's controller service
@@ -90,5 +90,5 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
9090
})
9191
ctx.Export("ingressIps", ingressIps)
9292

93-
return nil
93+
return ingressNginx, nil
9494
}

0 commit comments

Comments
 (0)