registry/internal/validators/validators.go
joan-anthropic 57efb58143
feat: Add support for MCP Bundles (MCPB) in registry (#295)
## Motivation and Context
- Add MCPB as a new registry_name option
- Add file_hashes field to package model for integrity verification
- Implement URL validation restricting MCPB packages to GitHub/GitLab
hosts
- Require SHA-256 hash for all MCPB packages
- Add example MCPB package in documentation
- Update schemas and OpenAPI specification

See issue #260

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

## Breaking Changes
N/A, registry has not yet been launched

## 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)
- [x] New feature (non-breaking change which adds functionality)
- [ ] 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)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [x] I have added or updated documentation as needed

---------

Co-authored-by: Adam Jones <adamj+git@anthropic.com>
2025-08-27 04:44:53 +01:00

124 lines
3.2 KiB
Go

package validators
import (
"fmt"
"github.com/modelcontextprotocol/registry/internal/model"
)
// ServerValidator validates server details
type ServerValidator struct {
*RepositoryValidator // Embedded RepositoryValidator for repository validation
}
// Validate checks if the server details are valid
func (v *ServerValidator) Validate(obj *model.ServerDetail) error {
if err := v.RepositoryValidator.Validate(&obj.Repository); err != nil {
return err
}
return nil
}
// NewServerValidator creates a new ServerValidator instance
func NewServerValidator() *ServerValidator {
return &ServerValidator{
RepositoryValidator: NewRepositoryValidator(),
}
}
// RepositoryValidator validates repository details
type RepositoryValidator struct {
validSources map[RepositorySource]bool
}
// Validate checks if the repository details are valid
func (rv *RepositoryValidator) Validate(obj *model.Repository) error {
// Skip validation for empty repository (optional field)
if obj.URL == "" && obj.Source == "" {
return nil
}
// validate the repository source
repoSource := RepositorySource(obj.Source)
if !IsValidRepositoryURL(repoSource, obj.URL) {
return fmt.Errorf("%w: %s", ErrInvalidRepositoryURL, obj.URL)
}
return nil
}
// NewRepositoryValidator creates a new RepositoryValidator instance
func NewRepositoryValidator() *RepositoryValidator {
return &RepositoryValidator{
validSources: map[RepositorySource]bool{SourceGitHub: true, SourceGitLab: true},
}
}
// PackageValidator validates package details
type PackageValidator struct{}
// Validate checks if the package details are valid
func (pv *PackageValidator) Validate(obj *model.Package) error {
if !HasNoSpaces(obj.Identifier) {
return ErrPackageNameHasSpaces
}
return nil
}
// NewPackageValidator creates a new PackageValidator instance
func NewPackageValidator() *PackageValidator {
return &PackageValidator{}
}
// RemoteValidator validates remote connection details
type RemoteValidator struct{}
// Validate checks if the remote connection details are valid
func (rv *RemoteValidator) Validate(obj *model.Remote) error {
if !IsValidURL(obj.URL) {
return fmt.Errorf("%w: %s", ErrInvalidRemoteURL, obj.URL)
}
return nil
}
// NewRemoteValidator creates a new RemoteValidator instance
func NewRemoteValidator() *RemoteValidator {
return &RemoteValidator{}
}
// ObjectValidator aggregates multiple validators for different object types
// This allows for a single entry point to validate complex objects that may contain multiple fields
// that need validation.
type ObjectValidator struct {
ServerValidator *ServerValidator
PackageValidator *PackageValidator
RemoteValidator *RemoteValidator
}
func NewObjectValidator() *ObjectValidator {
return &ObjectValidator{
ServerValidator: NewServerValidator(),
PackageValidator: NewPackageValidator(),
RemoteValidator: NewRemoteValidator(),
}
}
func (ov *ObjectValidator) Validate(obj *model.ServerDetail) error {
if err := ov.ServerValidator.Validate(obj); err != nil {
return err
}
for _, pkg := range obj.Packages {
if err := ov.PackageValidator.Validate(&pkg); err != nil {
return err
}
}
for _, remote := range obj.Remotes {
if err := ov.RemoteValidator.Validate(&remote); err != nil {
return err
}
}
return nil
}