Skip to content

Commit 8663235

Browse files
authored
Merge branch 'main' into adamj/fix-atomic-latest-version-update
2 parents 374595b + ae00f70 commit 8663235

File tree

13 files changed

+150
-21
lines changed

13 files changed

+150
-21
lines changed

.env.example

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ MCP_REGISTRY_SERVER_ADDRESS=:8080
55
MCP_REGISTRY_VERSION=dev
66

77
# Database configuration
8-
# Supported types: postgresql
9-
MCP_REGISTRY_DATABASE_TYPE=postgresql
108
MCP_REGISTRY_DATABASE_URL=postgres://username:password@localhost:5432/mcp-registry
119

1210
# Path or URL to import seed data (supports local files and HTTP URLs)

internal/config/config.go

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

7-
type DatabaseType string
8-
9-
const (
10-
DatabaseTypePostgreSQL DatabaseType = "postgresql"
11-
)
12-
137
// Config holds the application configuration
148
// See .env.example for more documentation
159
type Config struct {
1610
ServerAddress string `env:"SERVER_ADDRESS" envDefault:":8080"`
17-
DatabaseType DatabaseType `env:"DATABASE_TYPE" envDefault:"postgresql"`
1811
DatabaseURL string `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/mcp-registry?sslmode=disable"`
1912
SeedFrom string `env:"SEED_FROM" envDefault:""`
2013
Version string `env:"VERSION" envDefault:"dev"`

internal/validators/registries/mcpb.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,19 @@ import (
1212
"github.com/modelcontextprotocol/registry/pkg/model"
1313
)
1414

15+
var (
16+
ErrMissingIdentifierForMCPB = fmt.Errorf("package identifier is required for MCPB packages")
17+
ErrMissingFileSHA256ForMCPB = fmt.Errorf("must include a fileSha256 hash for integrity verification")
18+
)
19+
1520
func ValidateMCPB(ctx context.Context, pkg model.Package, _ string) error {
1621
// MCPB packages must include a file hash for integrity verification
1722
if pkg.FileSHA256 == "" {
18-
return fmt.Errorf("MCPB package must include a fileSha256 hash for integrity verification")
23+
return ErrMissingFileSHA256ForMCPB
24+
}
25+
26+
if pkg.Identifier == "" {
27+
return ErrMissingIdentifierForMCPB
1928
}
2029

2130
err := validateMCPBUrl(pkg.Identifier)

internal/validators/registries/mcpb_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ func TestValidateMCPB(t *testing.T) {
2020
expectError bool
2121
errorMessage string
2222
}{
23+
{
24+
name: "empty package identifier should fail",
25+
packageName: "",
26+
serverName: "com.example/test",
27+
fileSHA256: "abc123ef4567890abcdef1234567890abcdef1234567890abcdef1234567890",
28+
expectError: true,
29+
errorMessage: "package identifier is required for MCPB packages",
30+
},
31+
{
32+
name: "empty file SHA256 should fail",
33+
packageName: "https://github.com/example/server/releases/download/v1.0.0/server.mcpb",
34+
serverName: "com.example/test",
35+
fileSHA256: "",
36+
expectError: true,
37+
errorMessage: "must include a fileSha256 hash for integrity verification",
38+
},
39+
{
40+
name: "both empty identifier and file SHA256 should fail with file SHA256 error first",
41+
packageName: "",
42+
serverName: "com.example/test",
43+
fileSHA256: "",
44+
expectError: true,
45+
errorMessage: "must include a fileSha256 hash for integrity verification",
46+
},
2347
{
2448
name: "valid MCPB package should pass",
2549
packageName: "https://github.com/domdomegg/airtable-mcp-server/releases/download/v1.7.2/airtable-mcp-server.mcpb",

internal/validators/registries/npm.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package registries
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"net/http"
89
"net/url"
@@ -11,6 +12,11 @@ import (
1112
"github.com/modelcontextprotocol/registry/pkg/model"
1213
)
1314

15+
var (
16+
ErrMissingIdentifierForNPM = errors.New("package identifier is required for NPM packages")
17+
ErrMissingVersionForNPM = errors.New("package version is required for NPM packages")
18+
)
19+
1420
// NPMPackageResponse represents the structure returned by the NPM registry API
1521
type NPMPackageResponse struct {
1622
MCPName string `json:"mcpName"`
@@ -24,15 +30,15 @@ func ValidateNPM(ctx context.Context, pkg model.Package, serverName string) erro
2430
}
2531

2632
if pkg.Identifier == "" {
27-
return fmt.Errorf("package identifier is required for NPM packages")
33+
return ErrMissingIdentifierForNPM
2834
}
2935

3036
// we need version to look up the package metadata
3137
// not providing version will return all the versions
3238
// and we won't be able to validate the mcpName field
3339
// against the server name
3440
if pkg.Version == "" {
35-
return fmt.Errorf("package version is required for NPM packages")
41+
return ErrMissingVersionForNPM
3642
}
3743

3844
// Validate that the registry base URL matches NPM exactly

internal/validators/registries/nuget.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package registries
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io"
78
"net/http"
@@ -11,13 +12,22 @@ import (
1112
"github.com/modelcontextprotocol/registry/pkg/model"
1213
)
1314

15+
var (
16+
ErrMissingIdentifierForNuget = errors.New("package identifier is required for NuGet packages")
17+
ErrMissingVersionForNuget = errors.New("package version is required for NuGet packages")
18+
)
19+
1420
// ValidateNuGet validates that a NuGet package contains the correct MCP server name
1521
func ValidateNuGet(ctx context.Context, pkg model.Package, serverName string) error {
1622
// Set default registry base URL if empty
1723
if pkg.RegistryBaseURL == "" {
1824
pkg.RegistryBaseURL = model.RegistryURLNuGet
1925
}
2026

27+
if pkg.Identifier == "" {
28+
return ErrMissingIdentifierForNuget
29+
}
30+
2131
// Validate that the registry base URL matches NuGet exactly
2232
if pkg.RegistryBaseURL != model.RegistryURLNuGet {
2333
return fmt.Errorf("registry type and base URL do not match: '%s' is not valid for registry type '%s'. Expected: %s",
@@ -29,7 +39,7 @@ func ValidateNuGet(ctx context.Context, pkg model.Package, serverName string) er
2939
lowerID := strings.ToLower(pkg.Identifier)
3040
lowerVersion := strings.ToLower(pkg.Version)
3141
if lowerVersion == "" {
32-
return fmt.Errorf("NuGet package validation requires a specific version, but none was provided")
42+
return ErrMissingVersionForNuget
3343
}
3444

3545
// Try to get README from the package

internal/validators/registries/nuget_test.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ func TestValidateNuGet_RealPackages(t *testing.T) {
2020
expectError bool
2121
errorMessage string
2222
}{
23+
{
24+
name: "empty package identifier should fail",
25+
packageName: "",
26+
version: "1.0.0",
27+
serverName: "com.example/test",
28+
expectError: true,
29+
errorMessage: "package identifier is required for NuGet packages",
30+
},
31+
{
32+
name: "empty package version should fail",
33+
packageName: "test-package",
34+
version: "",
35+
serverName: "com.example/test",
36+
expectError: true,
37+
errorMessage: "package version is required for NuGet packages",
38+
},
39+
{
40+
name: "both empty identifier and version should fail with identifier error first",
41+
packageName: "",
42+
version: "",
43+
serverName: "com.example/test",
44+
expectError: true,
45+
errorMessage: "package identifier is required for NuGet packages",
46+
},
2347
{
2448
name: "non-existent package should fail",
2549
packageName: generateRandomNuGetPackageName(),
@@ -34,7 +58,7 @@ func TestValidateNuGet_RealPackages(t *testing.T) {
3458
version: "", // No version provided
3559
serverName: "com.example/test",
3660
expectError: true,
37-
errorMessage: "requires a specific version",
61+
errorMessage: "package version is required for NuGet packages",
3862
},
3963
{
4064
name: "real package with non-existent version should fail",

internal/validators/registries/oci.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import (
1313
"github.com/modelcontextprotocol/registry/pkg/model"
1414
)
1515

16+
var (
17+
ErrMissingIdentifierForOCI = errors.New("package identifier is required for OCI packages")
18+
ErrMissingVersionForOCI = errors.New("package version is required for OCI packages")
19+
)
20+
1621
const (
1722
dockerIoAPIBaseURL = "https://registry-1.docker.io"
1823
ghcrAPIBaseURL = "https://ghcr.io"
@@ -80,6 +85,15 @@ func ValidateOCI(ctx context.Context, pkg model.Package, serverName string) erro
8085
pkg.RegistryBaseURL = model.RegistryURLDocker
8186
}
8287

88+
if pkg.Identifier == "" {
89+
return ErrMissingIdentifierForOCI
90+
}
91+
92+
// we need version (tag) to look up the image manifest
93+
if pkg.Version == "" {
94+
return ErrMissingVersionForOCI
95+
}
96+
8397
// Validate that the registry base URL is supported
8498
if err := validateRegistryURL(pkg.RegistryBaseURL); err != nil {
8599
return err
@@ -258,7 +272,6 @@ func getRegistryAuthToken(ctx context.Context, client *http.Client, config *Regi
258272
return authResp.Token, nil
259273
}
260274

261-
262275
// getSpecificManifest retrieves a specific manifest for multi-arch images
263276
func getSpecificManifest(ctx context.Context, client *http.Client, registryConfig *RegistryConfig, namespace, repo, digest string) (*OCIManifest, error) {
264277
manifestURL := fmt.Sprintf("%s/v2/%s/%s/manifests/%s", registryConfig.APIBaseURL, namespace, repo, digest)

internal/validators/registries/oci_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,30 @@ func TestValidateOCI_RealPackages(t *testing.T) {
2121
errorMessage string
2222
registryURL string
2323
}{
24+
{
25+
name: "empty package identifier should fail",
26+
packageName: "",
27+
version: "latest",
28+
serverName: "com.example/test",
29+
expectError: true,
30+
errorMessage: "package identifier is required for OCI packages",
31+
},
32+
{
33+
name: "empty package version should fail",
34+
packageName: "test-image",
35+
version: "",
36+
serverName: "com.example/test",
37+
expectError: true,
38+
errorMessage: "package version is required for OCI packages",
39+
},
40+
{
41+
name: "both empty identifier and version should fail with identifier error first",
42+
packageName: "",
43+
version: "",
44+
serverName: "com.example/test",
45+
expectError: true,
46+
errorMessage: "package identifier is required for OCI packages",
47+
},
2448
{
2549
name: "non-existent image should fail",
2650
packageName: generateRandomImageName(),

internal/validators/registries/pypi.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package registries
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"net/http"
89
"strings"
@@ -11,6 +12,11 @@ import (
1112
"github.com/modelcontextprotocol/registry/pkg/model"
1213
)
1314

15+
var (
16+
ErrMissingIdentifierForPyPI = errors.New("package identifier is required for PyPI packages")
17+
ErrMissingVersionForPyPi = errors.New("package version is required for PyPI packages")
18+
)
19+
1420
// PyPIPackageResponse represents the structure returned by the PyPI JSON API
1521
type PyPIPackageResponse struct {
1622
Info struct {
@@ -25,6 +31,14 @@ func ValidatePyPI(ctx context.Context, pkg model.Package, serverName string) err
2531
pkg.RegistryBaseURL = model.RegistryURLPyPI
2632
}
2733

34+
if pkg.Identifier == "" {
35+
return ErrMissingIdentifierForPyPI
36+
}
37+
38+
if pkg.Version == "" {
39+
return ErrMissingVersionForPyPi
40+
}
41+
2842
// Validate that the registry base URL matches PyPI exactly
2943
if pkg.RegistryBaseURL != model.RegistryURLPyPI {
3044
return fmt.Errorf("registry type and base URL do not match: '%s' is not valid for registry type '%s'. Expected: %s",
@@ -33,7 +47,7 @@ func ValidatePyPI(ctx context.Context, pkg model.Package, serverName string) err
3347

3448
client := &http.Client{Timeout: 10 * time.Second}
3549

36-
url := fmt.Sprintf("%s/pypi/%s/json", pkg.RegistryBaseURL, pkg.Identifier)
50+
url := fmt.Sprintf("%s/pypi/%s/%s/json", pkg.RegistryBaseURL, pkg.Identifier, pkg.Version)
3751
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
3852
if err != nil {
3953
return fmt.Errorf("failed to create request: %w", err)

0 commit comments

Comments
 (0)