Repository: modelcontextprotocol/registry
File: internal/validators/validators.go (Go)

Code before:
	return nil
}

// collectAvailableVariables collects all available template variables from a package
func collectAvailableVariables(pkg *model.Package) []string {
	var variables []string

	// Add environment variable names
	for _, env := range pkg.EnvironmentVariables {
		variables = append(variables, env.Name)
	}

	// Add runtime argument names and value hints
	for _, arg := range pkg.RuntimeArguments {
		if arg.Name != "" {
			variables = append(variables, arg.Name)
		}
		if arg.ValueHint != "" {
			variables = append(variables, arg.ValueHint)
		}
	}

	// Add package argument names and value hints
	for _, arg := range pkg.PackageArguments {
		if arg.Name != "" {
			variables = append(variables, arg.Name)
		}
		if arg.ValueHint != "" {
			variables = append(variables, arg.ValueHint)
		}
	}

	return variables
}

// validatePackageTransport validates a package's transport with templating support
func validatePackageTransport(transport *model.Transport, availableVariables []string) error {
	// Validate transport type is supported
	switch transport.Type {
	case model.TransportTypeStdio:
		// Validate that URL is empty for stdio transport
		if transport.URL != "" {
			return fmt.Errorf("url must be empty for %s transport type, got: %s", transport.Type, transport.URL)
		}
		return nil
	case model.TransportTypeStreamableHTTP, model.TransportTypeSSE:
		// URL is required for streamable-http and sse
		if transport.URL == "" {
			return fmt.Errorf("url is required for %s transport type", transport.Type)
		}
		// Validate URL format with template variable support

Review comment (suggestion):
```suggestion
// looksLikeVersionRange detects common semver range syntaxes and wildcard patterns.
// Examples that should return true:
// - "^1.2.3",
// - "~1.2.3",
// - ">=1.0.0",
// - "1.x",
// - "1.2.*",
// - "1 - 2",
// - "1.2 || 1.3"
func looksLikeVersionRange(version string) bool {
	s := strings.TrimSpace(version)
	if s == "" {
		return false
	}

	if isComparatorRange(s) || isHyphenRange(s) || isOrRange(s) || isWildcardDotted(s) {
		return true
	}

	return false
}

func isComparatorRange(s string) bool {
	for _, cmp := range []string{"^", "~", ">=", "<=", ">", "<", "="} {
		if strings.HasPrefix(s, cmp) {
			rhs := strings.TrimSpace(strings.TrimPrefix(s, cmp))
			return semver.IsValid(v(rhs))
		}
	}
	return false
}

func isHyphenRange(s string) bool {
	// exact “a - b” (one hyphen with spaces)
	const sep = " - "
	if !strings.Contains(s, sep) {
		return false
	}
	parts := strings.Split(s, sep)
	if len(parts) != 2 {
		return false
	}
	return semver.IsValid(v(parts[0])) && semver.IsValid(v(parts[1]))
}

func isOrRange(s string) bool {
	if !strings.Contains(s, "||") {
		return false
	}
	parts := strings.Split(s, "||")
	semverSides := 0
	for _, p := range parts {
		if semver.IsValid(v(strings.TrimSpace(p))) {
			semverSides++
			if semverSides >= 2 {
				return true
			}
		}
	}
	return false
}

func isWildcardDotted(s string) bool {
	// quick dotted-shape check, then look for wildcard segment
	if !strings.Contains(s, ".") {
		return false
	}
	for _, seg := range strings.Split(s, ".") {
		seg = strings.TrimSpace(seg)
		if seg == "x" || seg == "X" || seg == "*" {
			return true
		}
	}
	return false
}

// v adds a "v" prefix if not present
func v(version string) string {
	if !strings.HasPrefix(version, "v") {
		return "v" + version
	}
	return version
}

```

Code after:
// - "1.x",
// - "1.2.*",
// - "1 - 2",
// - "1.2 || 1.3"
func looksLikeVersionRange(version string) bool {
	trimmed := strings.TrimSpace(version)
	if trimmed == "" {
		return false
	}

	if comparatorRangeRe.MatchString(trimmed) {
		return true
	}
	if hyphenRangeRe.MatchString(trimmed) {
		return true
	}
	if orRangeRe.MatchString(trimmed) {
		return true
	}
	if dottedVersionLikeRe.MatchString(trimmed) {
		// wildcard in a dotted version (x/X/*) implies range-like intent
		return strings.Contains(trimmed, "x") || strings.Contains(trimmed, "X") || strings.Contains(trimmed, "*")
	}
	return false
}

// validateArgument validates argument details
func validateArgument(obj *model.Argument) error {
	if obj.Type == model.ArgumentTypeNamed {
		// Validate named argument name format
		if err := validateNamedArgumentName(obj.Name); err != nil {
			return err
		}

		// Validate value and default don't start with the name
		if err := validateArgumentValueFields(obj.Name, obj.Value, obj.Default); err != nil {
			return err
		}
	}
	return nil
}

func validateNamedArgumentName(name string) error {
	// Check if name is required for named arguments
	if name == "" {
		return ErrNamedArgumentNameRequired
	}

	// Check for invalid characters that suggest embedded values or descriptions
	// Valid: "--directory", "--port", "-v", "config", "verbose"
	// Invalid: "--directory <absolute_path_to_adfin_mcp_folder>", "--port 8080"