@@ -12,6 +12,7 @@ import (
1212 "github.com/google/uuid"
1313 upstream "github.com/modelcontextprotocol/registry/pkg/api/v0"
1414 "github.com/modelcontextprotocol/registry/pkg/model"
15+ "github.com/xeipuuv/gojsonschema"
1516
1617 "github.com/stacklok/toolhive-registry/pkg/types"
1718)
@@ -29,10 +30,16 @@ func NewOfficialRegistry(loader *Loader) *OfficialRegistry {
2930}
3031
3132// WriteJSON builds the official MCP registry and writes it to the specified path
33+ // The registry is validated against the schema before writing - generation fails if validation fails
3234func (or * OfficialRegistry ) WriteJSON (path string ) error {
3335 // Build the registry structure
3436 registry := or .build ()
3537
38+ // Validate the registry before writing
39+ if err := or .validateRegistry (registry ); err != nil {
40+ return fmt .Errorf ("registry validation failed: %w" , err )
41+ }
42+
3643 // Create the directory if it doesn't exist
3744 dir := filepath .Dir (path )
3845 if err := os .MkdirAll (dir , 0750 ); err != nil {
@@ -53,6 +60,53 @@ func (or *OfficialRegistry) WriteJSON(path string) error {
5360 return nil
5461}
5562
63+ // ValidateAgainstSchema validates the built registry against the schema
64+ func (or * OfficialRegistry ) ValidateAgainstSchema () error {
65+ registry := or .build ()
66+ return or .validateRegistry (registry )
67+ }
68+
69+ // validateRegistry validates a registry object against the schema
70+ func (* OfficialRegistry ) validateRegistry (registry * ToolHiveRegistryType ) error {
71+ // Marshal registry to JSON
72+ registryJSON , err := json .Marshal (registry )
73+ if err != nil {
74+ return fmt .Errorf ("failed to marshal registry: %w" , err )
75+ }
76+
77+ // Load schema from local file (fallback to remote if needed)
78+ schemaPath := "schemas/registry.schema.json"
79+ var schemaLoader gojsonschema.JSONLoader
80+
81+ // Try local schema first
82+ if _ , err := os .Stat (schemaPath ); err == nil {
83+ schemaLoader = gojsonschema .NewReferenceLoader ("file://" + schemaPath )
84+ } else {
85+ // Fall back to remote schema
86+ schemaLoader = gojsonschema .NewReferenceLoader (
87+ "https://raw.githubusercontent.com/stacklok/toolhive-registry/main/schemas/registry.schema.json" )
88+ }
89+
90+ // Create document loader from registry data
91+ documentLoader := gojsonschema .NewBytesLoader (registryJSON )
92+
93+ // Perform validation
94+ result , err := gojsonschema .Validate (schemaLoader , documentLoader )
95+ if err != nil {
96+ return fmt .Errorf ("schema validation failed: %w" , err )
97+ }
98+
99+ if ! result .Valid () {
100+ var errorMessages []string
101+ for _ , desc := range result .Errors () {
102+ errorMessages = append (errorMessages , desc .String ())
103+ }
104+ return fmt .Errorf ("validation errors: %v" , errorMessages )
105+ }
106+
107+ return nil
108+ }
109+
56110// build creates the ToolHiveRegistryType structure from loaded entries
57111func (or * OfficialRegistry ) build () * ToolHiveRegistryType {
58112 entries := or .loader .GetEntries ()
@@ -73,7 +127,7 @@ func (or *OfficialRegistry) build() *ToolHiveRegistryType {
73127 }
74128
75129 registry := & ToolHiveRegistryType {
76- Schema : "" , // TODO: Add schema URL once applicable
130+ Schema : "https://raw.githubusercontent.com/stacklok/toolhive-registry/main/schemas/registry. schema.json" ,
77131 Version : "1.0.0" ,
78132 Meta : Meta {
79133 LastUpdated : time .Now ().UTC ().Format (time .RFC3339 ),
0 commit comments