Adds an optional `subfolder` field to the Repository object in server.json schema to support MCP servers located within subdirectories of monorepo structures. ## Motivation and Context Addresses issue #360 . The current MCP registry schema doesn't explicitly define how to reference servers that are located within a subdirectory of a Git repository (a "monorepo" structure). This is a common pattern (e.g., [microsoft/mcp](https://github.com/microsoft/mcp) & [awslabs/mcp](https://github.com/awslabs/mcp). ## How Has This Been Tested? - **Validator tests**: Added 6 new test cases covering valid subfolders and security validation (path traversal, absolute paths, invalid characters, etc.) - **Schema validation**: All 11 examples in `examples.md` validate successfully against both `server.schema.json` and `registry-schema.json` - **End-to-end validation**: Publisher tool (`mcp-publisher init`) works correctly and generates valid templates without subfolder - **Backward compatibility**: Existing server.json files without subfolder continue to work unchanged ## Breaking Changes **None.** This is a fully backward-compatible change: - The `subfolder` field is optional (uses `omitempty` JSON tag) - Existing server.json files work unchanged - Publisher tools generate valid templates without subfolder by default - All existing validation and schemas remain compatible ## Types of changes - [ ] 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) - [x] Documentation update ## Checklist - [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 ### Implementation Details **Schema Changes:** - Added `subfolder` property to Repository definition in `server.schema.json` and `openapi.yaml` - Field is optional with description and example (`"src/everything"`) **Go Model:** - Added `Subfolder string` field to Repository struct with `json:"subfolder,omitempty"` tag - Maintains backward compatibility through omitempty behavior **Security-Focused Validation:** - Validates subfolder is a clean relative path (no leading `/`) - Prevents path traversal attacks (no `..` sequences) - Blocks malformed paths (no trailing `/`, empty segments, invalid characters) - Uses regex validation for allowed characters: `^[a-zA-Z0-9\-_./]+$` **Documentation:** - Added monorepo example to `examples.md` demonstrating subfolder usage - Updated `repository_references.md` with concise subfolder documentation - Maintained consistency across all documentation with same example path **Testing:** - 6 new integration tests in validator covering security and format validation - Updated example count validation tool for new example - All existing tests continue to pass ### Design Decisions 1. **Optional by design**: Most servers don't need subfolder (single-repo pattern is common) 2. **Clean separation**: Repository URL stays clean, subfolder is separate metadata 3. **Security first**: Comprehensive validation prevents common path-based attacks 4. **Publisher tool unchanged**: Default templates don't include subfolder to avoid confusion for typical use cases This enables publishers with monorepo structures to clearly specify server locations while maintaining simplicity for the common single-repository case.
34 lines
1.2 KiB
Go
34 lines
1.2 KiB
Go
package validators
|
|
|
|
import "errors"
|
|
|
|
// Error messages for validation
|
|
var (
|
|
// Repository validation errors
|
|
ErrInvalidRepositoryURL = errors.New("invalid repository URL")
|
|
ErrInvalidSubfolderPath = errors.New("invalid subfolder path")
|
|
|
|
// Package validation errors
|
|
ErrPackageNameHasSpaces = errors.New("package name cannot contain spaces")
|
|
|
|
// Remote validation errors
|
|
ErrInvalidRemoteURL = errors.New("invalid remote URL")
|
|
|
|
// Registry validation errors
|
|
ErrUnsupportedRegistryBaseURL = errors.New("unsupported registry base URL")
|
|
ErrMismatchedRegistryTypeAndURL = errors.New("registry type and base URL do not match")
|
|
|
|
// Argument validation errors
|
|
ErrNamedArgumentNameRequired = errors.New("named argument name is required")
|
|
ErrInvalidNamedArgumentName = errors.New("invalid named argument name format")
|
|
ErrArgumentValueStartsWithName = errors.New("argument value cannot start with the argument name")
|
|
ErrArgumentDefaultStartsWithName = errors.New("argument default cannot start with the argument name")
|
|
)
|
|
|
|
// RepositorySource represents valid repository sources
|
|
type RepositorySource string
|
|
|
|
const (
|
|
SourceGitHub RepositorySource = "github"
|
|
SourceGitLab RepositorySource = "gitlab"
|
|
)
|