// Copyright 2025 The Go MCP SDK Authors. All rights reserved. // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // This file deals with preparing a schema for validation, including various checks, // optimizations, and the resolution of cross-schema references. package jsonschema import ( "errors" "fmt" "regexp" ) // A Resolved consists of a [Schema] along with associated information needed to // validate documents against it. // A Resolved has been validated against its meta-schema, and all its references // (the $ref and $dynamicRef keywords) have been resolved to their referenced Schemas. // Call [Schema.Resolve] to obtain a Resolved from a Schema. type Resolved struct { root *Schema } // Resolve resolves all references within the schema and performs other tasks that // prepare the schema for validation. func (root *Schema) Resolve() (*Resolved, error) { // There are three steps involved in preparing a schema to validate. // 1. Check: validate the schema against a meta-schema, and perform other well-formedness // checks. Precompute some values along the way. // 2. Resolve URIs: TODO. // 3. Resolve references: TODO. if err := root.check(); err != nil { return nil, err } return &Resolved{root: root}, nil } func (s *Schema) check() error { if s == nil { return errors.New("nil schema") } var errs []error report := func(err error) { errs = append(errs, err) } for ss := range s.all() { ss.checkLocal(report) } return errors.Join(errs...) } // checkLocal checks s for validity, independently of other schemas it may refer to. // Since checking a regexp involves compiling it, checkLocal saves those compiled regexps // in the schema for later use. // It appends the errors it finds to errs. func (s *Schema) checkLocal(report func(error)) { addf := func(format string, args ...any) { report(fmt.Errorf("jsonschema.Schema: "+format, args...)) } if s == nil { addf("nil subschema") return } if err := s.basicChecks(); err != nil { report(err) return } // TODO: validate the schema's properties, // ideally by jsonschema-validating it against the meta-schema. // Check and compile regexps. if s.Pattern != "" { re, err := regexp.Compile(s.Pattern) if err != nil { addf("pattern: %w", err) } else { s.pattern = re } } if len(s.PatternProperties) > 0 { s.patternProperties = map[*regexp.Regexp]*Schema{} for reString, subschema := range s.PatternProperties { re, err := regexp.Compile(reString) if err != nil { addf("patternProperties[%q]: %w", reString, err) continue } s.patternProperties[re] = subschema } } }