registry/tools/validate-schemas/main.go
Tadas Antanavicius 774a0df592
Add validation tools for server.json schemas and examples (#176)
This is a first PR for #159 - adding some validation scripts. I want to
get to a place where we are confident that making schema changes is in
sync with the rest of our code. Step one is making sure the schemas are
in sync with our examples. Step two will be making sure the schemas are
in sync with our actual Registry implementation code.

This is just for the JSON right now; if we're feeling good about this
approach I'll do an analogous change for the OpenAPI schemas as well.

I'll also follow with adding usage of these scripts to our CI pipeline
so we have automated checks on them with every change.

## Summary
- Added CLI validation tools for JSON schemas and examples in
`docs/server-json/`
- Fixed some minor validation errors found in `examples.md`
- Ensures schema consistency and example validity in CI/CD pipelines

## Changes

### New Validation Tools
1. **`tools/validate-schemas`** - Validates that `schema.json` and
`registry-schema.json` are valid JSON Schema documents
2. **`tools/validate-examples`** - Validates all JSON examples in
`examples.md` against both schemas

### Key Features
- ✅ Validates JSON schema syntax and structure
- ✅ Validates all examples against both base and registry schemas
- ✅ Tracks validation counts to prevent silent failures
- ✅ Proper exit codes for CI integration
- ✅ Clear error messages for debugging

### Fixes to examples.md
- Added missing `repository.id` field in NuGet example
- Changed `repository.source` from "gitlab" to "github" in Docker
example

### Usage
```bash
# From repository root
./tools/validate-schemas.sh
./tools/validate-examples.sh
```

## Testing

I ran these scripts locally with minor (breaking) changes, which they
each correctly reported. As submitted, they are both passing.

🤖 Co-authored with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Charles Lowell <10964656+chlowell@users.noreply.github.com>
2025-07-15 11:32:54 -07:00

94 lines
2.3 KiB
Go

// validate-schemas validates that schema.json and registry-schema.json
// are valid JSON Schema documents.
//
// For more information, see docs/server-json/README.md
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/santhosh-tekuri/jsonschema/v5"
)
func main() {
log.SetFlags(0) // Remove timestamp from logs
if err := runValidation(); err != nil {
log.Fatalf("Error: %v", err)
}
}
func runValidation() error {
basePath := filepath.Join("docs", "server-json")
schemas := []struct {
name string
path string
}{
{"schema.json", filepath.Join(basePath, "schema.json")},
{"registry-schema.json", filepath.Join(basePath, "registry-schema.json")},
}
expectedSchemaCount := len(schemas)
validatedCount := 0
for _, schemaFile := range schemas {
log.Printf("Validating %s...", schemaFile.name)
if err := validateSchema(schemaFile.path); err != nil {
log.Printf(" ❌ Invalid: %v", err)
} else {
log.Printf(" ✅ Valid JSON Schema")
validatedCount++
}
}
if validatedCount != expectedSchemaCount {
return fmt.Errorf("validation failed: expected to validate %d schemas but only %d passed",
expectedSchemaCount, validatedCount)
}
log.Printf("\nSuccessfully validated all %d schemas!", validatedCount)
return nil
}
func validateSchema(path string) error {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read file: %w", err)
}
var schemaData any
if err := json.Unmarshal(data, &schemaData); err != nil {
return fmt.Errorf("invalid JSON: %w", err)
}
compiler := jsonschema.NewCompiler()
compiler.Draft = jsonschema.Draft7
// For registry-schema.json, we need to register the base schema it references
if strings.Contains(path, "registry-schema.json") {
basePath := filepath.Join(filepath.Dir(path), "schema.json")
baseData, err := os.ReadFile(basePath)
if err != nil {
return fmt.Errorf("failed to read base schema: %w", err)
}
// Add the base schema to the compiler with the expected URL
if err := compiler.AddResource("https://modelcontextprotocol.io/schemas/draft/2025-07-09/server.json", bytes.NewReader(baseData)); err != nil {
return fmt.Errorf("failed to add base schema resource: %w", err)
}
}
if _, err := compiler.Compile(path); err != nil {
return fmt.Errorf("invalid schema: %w", err)
}
return nil
}