///|
/// The JSON Schema dialect used by MCP `2026-07-28` when `$schema` is absent.
pub const DRAFT_2020_12 : String = "https://json-schema.org/draft/2020-12/schema"
///|
/// Errors raised while constructing, compiling, or validating a schema.
pub suberror SchemaError {
InvalidDocument(String)
UnsupportedDialect(String)
UnsupportedKeyword(String)
InvalidSchema(String)
} derive(Debug, Eq)
///|
pub impl Show for SchemaError with fn output(
self : SchemaError,
logger : &Logger,
) -> Unit {
match self {
InvalidDocument(msg) => logger.write_string("InvalidDocument: \{msg}")
UnsupportedDialect(msg) => logger.write_string("UnsupportedDialect: \{msg}")
UnsupportedKeyword(msg) => logger.write_string("UnsupportedKeyword: \{msg}")
InvalidSchema(msg) => logger.write_string("InvalidSchema: \{msg}")
}
}
///|
/// JSON Schema provider for typed tool inputs.
pub(open) trait JsonSchema {
fn json_schema() -> Schema raise SchemaError
}
///|
pub impl JsonSchema for String with fn json_schema() -> Schema {
Schema::string() catch {
_ => Schema::always()
}
}
///|
pub impl JsonSchema for Bool with fn json_schema() -> Schema {
Schema::boolean()
}
///|
pub impl JsonSchema for Int with fn json_schema() -> Schema {
Schema::integer() catch {
_ => Schema::always()
}
}
///|
pub impl JsonSchema for Int64 with fn json_schema() -> Schema {
Schema::integer() catch {
_ => Schema::always()
}
}
///|
pub impl JsonSchema for Double with fn json_schema() -> Schema {
Schema::number() catch {
_ => Schema::always()
}
}
///|
/// A raw JSON value has no narrower instance constraint.
pub impl JsonSchema for Json with fn json_schema() -> Schema {
Schema::always()
}
///|
pub impl[T : JsonSchema] JsonSchema for Array[T] with fn json_schema() -> Schema raise SchemaError {
Schema::array(items=schema_of((None : T?)))
}
///|
pub impl[T : JsonSchema] JsonSchema for T? with fn json_schema() -> Schema raise SchemaError {
Schema::any_of([Schema::null(), schema_of((None : T?))])
}
///|
/// Obtain a typed schema without constructing a runtime value of `A`.
pub fn[A : JsonSchema] schema_of(_witness : A?) -> Schema raise SchemaError {
A::json_schema()
}
///|
/// Validates a JSON instance against the schema associated with type `T`,
/// and decodes it into `T`.
pub fn[T : JsonSchema + @json.FromJson] decode(
json : Json,
max_depth? : Int = 128,
) -> T raise Error {
let schema = schema_of((None : T?))
schema.validate(json, max_depth~)
@json.from_json(json)
}