///|
/// Package-private wire codec used to keep one JSON value's schema and
/// decoder together.
priv struct JsonCodec[T] {
schema : Json
decode : (Json) -> Result[T, String]
}
///|
/// Type-erased input codec used by the heterogeneous MCP tool registry.
priv struct ToolInputCodec {
schema : Json
decode : (Map[String, Json]) -> Result[ToolInvocation, String]
}
///|
/// Type-erased output codec. Pairing the output schema with its dispatcher
/// prevents a registry entry from selecting one without the other.
priv struct ToolOutputCodec[Host] {
schema : Json
dispatch : (Host, Json, ToolInvocation) -> Json
}
///|
priv struct ToolSpec[Host] {
name : String
description : String
input : ToolInputCodec
output : ToolOutputCodec[Host]
}
///|
fn object_schema(properties : Json, required? : Array[String] = []) -> Json {
(
{
"type": "object",
"properties": properties,
"required": required,
"additionalProperties": false,
} : Json)
}
///|
fn one_of_schema(options : Array[Json]) -> Json {
({ "oneOf": options } : Json)
}
///|
fn nullable_schema(value_schema : Json) -> Json {
one_of_schema([value_schema, ({ "type": "null" } : Json)])
}
///|
fn array_schema(item_schema : Json) -> Json {
({ "type": "array", "items": item_schema } : Json)
}
///|
fn tagged_empty_schema(kind : String) -> Json {
object_schema(({ "kind": { "const": kind } } : Json), required=["kind"])
}
///|
// This regular expression accepts exactly the canonical decimal spellings in
// UInt64's closed range. The decoder uses the same canonicality rules and also
// performs overflow-checked accumulation.
let uint64_decimal_pattern : String = "^(0|[1-9][0-9]{0,18}|1[0-7][0-9]{18}|18[0-3][0-9]{17}|184[0-3][0-9]{16}|1844[0-5][0-9]{15}|18446[0-6][0-9]{14}|184467[0-3][0-9]{13}|1844674[0-3][0-9]{12}|184467440[0-6][0-9]{10}|1844674407[0-2][0-9]{9}|18446744073[0-6][0-9]{8}|1844674407370[0-8][0-9]{6}|18446744073709[0-4][0-9]{5}|184467440737095[0-4][0-9]{4}|18446744073709550[0-9]{3}|18446744073709551[0-5][0-9]{2}|1844674407370955160[0-9]|1844674407370955161[0-5])$"
///|
let positive_uint64_decimal_pattern : String = "^([1-9][0-9]{0,18}|1[0-7][0-9]{18}|18[0-3][0-9]{17}|184[0-3][0-9]{16}|1844[0-5][0-9]{15}|18446[0-6][0-9]{14}|184467[0-3][0-9]{13}|1844674[0-3][0-9]{12}|184467440[0-6][0-9]{10}|1844674407[0-2][0-9]{9}|18446744073[0-6][0-9]{8}|1844674407370[0-8][0-9]{6}|18446744073709[0-4][0-9]{5}|184467440737095[0-4][0-9]{4}|18446744073709550[0-9]{3}|18446744073709551[0-5][0-9]{2}|1844674407370955160[0-9]|1844674407370955161[0-5])$"
///|
fn parse_decimal_u64(value : String) -> UInt64? {
if value == "" || (value.length() > 1 && value[0] == '0') {
return None
}
let mut parsed = 0UL
for character in value {
let code = character.to_int()
if code < '0' || code > '9' {
return None
}
let digit = (code - ('0' : Int)).to_uint64()
if parsed > (18446744073709551615UL - digit) / 10UL {
return None
}
parsed = parsed * 10UL + digit
}
Some(parsed)
}
///|
fn decimal_u64_codec(positive? : Bool = false) -> JsonCodec[UInt64] {
let expected = if positive {
"a canonical positive UInt64 decimal string"
} else {
"a canonical UInt64 decimal string"
}
{
schema: (
{
"type": "string",
"pattern": if positive {
positive_uint64_decimal_pattern
} else {
uint64_decimal_pattern
},
"minLength": 1,
"maxLength": 20,
"description": expected,
} : Json),
decode: value => {
match value {
String(text) =>
match parse_decimal_u64(text) {
Some(number) if !positive || number > 0UL => Ok(number)
_ => Err("Expected \{expected}.")
}
_ => Err("Expected \{expected}.")
}
},
}
}
///|
// JSON Schema counts Unicode characters rather than encoded bytes. The custom
// keyword carries the exact byte constraint used by SemanticId::parse, while
// the standard pattern mirrors its complete whitespace/control exclusion.
let semantic_id_pattern : String = "^[^\\u0000-\\u0020\\u007F-\\u009F\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+$"
///|
fn semantic_id_codec() -> JsonCodec[@core.SemanticId] {
{
schema: (
{
"type": "string",
"pattern": semantic_id_pattern,
"minLength": 1,
"maxLength": 255,
"x-moui-utf8ByteLength": { "minimum": 1, "maximum": 255 },
"description": "An exact SemanticId containing 1-255 UTF-8 bytes and no whitespace or control characters.",
} : Json),
decode: value => {
match value {
String(text) =>
match @core.SemanticId::parse(text) {
Some(id) => Ok(id)
None => Err("Invalid semantic_id.")
}
_ => Err("Invalid semantic_id.")
}
},
}
}
///|
fn semantics_target_codec() -> JsonCodec[@core.SemanticsTarget] {
let semantic_id = semantic_id_codec()
let node_id = decimal_u64_codec(positive=true)
{
schema: one_of_schema([
object_schema(
({ "kind": { "const": "semantic_id" }, "id": semantic_id.schema } : Json),
required=["kind", "id"],
),
object_schema(
({ "kind": { "const": "node_id" }, "id": node_id.schema } : Json),
required=["kind", "id"],
),
object_schema(
(
{
"kind": { "const": "path" },
"path": array_schema(semantics_path_segment_schema()),
} : Json),
required=["kind", "path"],
),
]),
decode: value => {
match value {
Object(fields) =>
match json_string_field(fields, "kind") {
Some("path") => decode_semantics_path_target(fields, semantic_id)
_ => decode_target_with_codecs(fields, semantic_id, node_id)
}
_ => Err("Missing tagged 'target' object.")
}
},
}
}
///|
fn semantics_path_segment_schema() -> Json {
object_schema(
(
{
"index": { "type": "integer", "minimum": 0 },
"role": semantics_role_schema(),
"label": { "type": "string" },
"value": { "type": "string" },
"semantic_id": semantic_id_codec().schema,
} : Json),
)
}
///|
fn decode_semantics_path_target(
fields : Map[String, Json],
semantic_id : JsonCodec[@core.SemanticId],
) -> Result[@core.SemanticsTarget, String] {
if !fields_are_exact(fields, ["kind", "path"]) {
return Err("Target contains unknown fields.")
}
guard fields.get("path") is Some(Array(steps)) else {
return Err("path target requires an array of steps.")
}
if steps.length() == 0 {
return Err("path target requires at least one step.")
}
let segments : Array[@core.SemanticsPathSegment] = []
for step in steps {
match step {
Object(segment_fields) =>
match decode_semantics_path_segment(segment_fields, semantic_id) {
Ok(segment) => segments.push(segment)
Err(message) => return Err(message)
}
_ => return Err("Each path step must be an object.")
}
}
Ok(ByPath(segments))
}
///|
fn decode_semantics_path_segment(
fields : Map[String, Json],
semantic_id : JsonCodec[@core.SemanticId],
) -> Result[@core.SemanticsPathSegment, String] {
let allowed = ["index", "role", "label", "value", "semantic_id"]
if !fields_are_exact(fields, allowed) {
return Err("Path step contains unknown fields.")
}
let index = match fields.get("index") {
None => 0
Some(Number(value, repr=_)) if value >= 0.0 &&
value == value.to_int().to_double() => value.to_int()
Some(_) => return Err("Path step 'index' must be a non-negative integer.")
}
let role = match fields.get("role") {
None => None
Some(String(text)) =>
match decode_semantics_path_role(text) {
Some(value) => Some(value)
None =>
if text == "none" {
return Err(
"Path step cannot filter on the 'none' role; filter on 'label' or 'value' instead.",
)
} else {
return Err("Invalid path step role.")
}
}
Some(_) => return Err("Path step 'role' must be a string.")
}
let label = match fields.get("label") {
None => None
Some(String(value)) => Some(value)
Some(_) => return Err("Path step 'label' must be a string.")
}
let value = match fields.get("value") {
None => None
Some(String(value)) => Some(value)
Some(_) => return Err("Path step 'value' must be a string.")
}
let semantic_id_filter = match fields.get("semantic_id") {
None => None
Some(semantic_id_json) =>
match (semantic_id.decode)(semantic_id_json) {
Ok(id) => Some(id)
Err(message) => return Err(message)
}
}
if role is None &&
label is None &&
value is None &&
semantic_id_filter is None {
return Err(
"Path step must filter on at least one of 'role', 'label', 'value', or 'semantic_id'.",
)
}
Ok({ index, role, label, value, semantic_id: semantic_id_filter, })
}
///|
fn decode_semantics_path_role(text : String) -> @core.SemanticsRole? {
match text {
"text" => Some(Text)
"link" => Some(Link)
"heading" => Some(Heading)
"navigation" => Some(Navigation)
"main" => Some(Main)
"button" => Some(Button)
"checkbox" => Some(Checkbox)
"text_field" => Some(TextField)
"list" => Some(List)
"list_item" => Some(ListItem)
"grid" => Some(Grid)
"scroll_view" => Some(ScrollView)
"switch" => Some(Switch)
"radio" => Some(Radio)
"slider" => Some(Slider)
"progress" => Some(Progress)
"tab" => Some(Tab)
"dialog" => Some(Dialog)
"tooltip" => Some(Tooltip)
"menu" => Some(Menu)
"button_group" => Some(ButtonGroup)
"form" => Some(Form)
"form_field" => Some(FormField)
"tree" => Some(Tree)
"tree_item" => Some(TreeItem)
"group" => Some(Group)
"web_view" => Some(WebView)
"image" => Some(Image)
"alert" => Some(Alert)
"status" => Some(Status)
"combo_box" => Some(ComboBox)
"option" => Some(Option)
"table" => Some(Table)
"row" => Some(Row)
"cell" => Some(Cell)
"separator" => Some(Separator)
_ => None
}
}
///|
fn semantics_action_codec() -> JsonCodec[@core.SemanticsAction] {
{
schema: one_of_schema([
tagged_empty_schema("activate"),
tagged_empty_schema("focus"),
object_schema(
(
{ "kind": { "const": "set_text" }, "value": { "type": "string" } } :
Json),
required=["kind", "value"],
),
tagged_empty_schema("submit"),
object_schema(
(
{
"kind": { "const": "scroll" },
"direction": {
"type": "string",
"enum": ["forward", "backward", "up", "down", "left", "right"],
},
} : Json),
required=["kind", "direction"],
),
tagged_empty_schema("select"),
tagged_empty_schema("expand"),
tagged_empty_schema("collapse"),
tagged_empty_schema("dismiss"),
tagged_empty_schema("increment"),
tagged_empty_schema("decrement"),
object_schema(
(
{
"kind": { "const": "set_numeric_value" },
"value": { "type": "number" },
} : Json),
required=["kind", "value"],
),
object_schema(
(
{
"kind": { "const": "set_selection" },
"start": { "type": "integer", "minimum": 0 },
"end": { "type": "integer", "minimum": 0 },
} : Json),
required=["kind", "start", "end"],
),
tagged_empty_schema("show_menu"),
]),
decode: value => {
match value {
Object(fields) => decode_action(fields)
_ => Err("Missing tagged 'action' object.")
}
},
}
}
///|
fn generation_precondition_codec() -> JsonCodec[
@agent.AgentGenerationPrecondition,
] {
let generation = decimal_u64_codec()
{
schema: one_of_schema([
tagged_empty_schema("latest"),
object_schema(
(
{ "kind": { "const": "exact" }, "generation": generation.schema } :
Json),
required=["kind", "generation"],
),
]),
decode: value => {
match value {
Object(fields) => decode_precondition_with_codec(fields, generation)
_ => Err("Missing tagged 'precondition' object.")
}
},
}
}
///|
fn read_semantics_input_codec() -> ToolInputCodec {
let generation = decimal_u64_codec()
{
schema: object_schema(({ "since": generation.schema } : Json)),
decode: arguments => {
decode_read_semantics_invocation_with_codec(arguments, generation)
},
}
}
///|
fn perform_action_input_codec() -> ToolInputCodec {
let target = semantics_target_codec()
let action = semantics_action_codec()
let precondition = generation_precondition_codec()
{
schema: object_schema(
(
{
"target": target.schema,
"action": action.schema,
"precondition": precondition.schema,
} : Json),
required=["target", "action", "precondition"],
),
decode: arguments => {
decode_action_request_with_codecs(arguments, target, action, precondition)
},
}
}
///|
fn tool_output_schema(value_schema : Json) -> Json {
one_of_schema([
object_schema(({ "ok": { "const": true }, "value": value_schema } : Json), required=[
"ok", "value",
]),
object_schema(
(
{
"ok": { "const": false },
"error": object_schema(
(
{
"code": { "type": "string" },
"message": { "type": "string" },
"details": { "type": "object" },
} : Json),
required=["code", "message", "details"],
),
} : Json),
required=["ok", "error"],
),
])
}
///|
fn semantics_state_schema() -> Json {
let boolean = ({ "type": "boolean" } : Json)
object_schema(
(
{
"focused": boolean,
"disabled": boolean,
"selected": boolean,
"pressed": boolean,
"checked": nullable_schema(
(
{ "type": "string", "enum": ["unchecked", "checked", "mixed"] } :
Json),
),
"expanded": boolean,
"invalid": boolean,
"required": boolean,
"read_only": boolean,
"busy": boolean,
"multiline": boolean,
"password": boolean,
"modal": boolean,
} : Json),
required=[
"focused", "disabled", "selected", "pressed", "checked", "expanded", "invalid",
"required", "read_only", "busy", "multiline", "password", "modal",
],
)
}
///|
fn point_schema() -> Json {
let number = ({ "type": "number" } : Json)
object_schema(({ "x": number, "y": number } : Json), required=["x", "y"])
}
///|
fn size_schema() -> Json {
let number = ({ "type": "number" } : Json)
object_schema(({ "width": number, "height": number } : Json), required=[
"width", "height",
])
}
///|
fn rect_schema() -> Json {
object_schema(({ "origin": point_schema(), "size": size_schema() } : Json), required=[
"origin", "size",
])
}
///|
fn text_range_schema() -> Json {
let index = ({ "type": "integer", "minimum": 0 } : Json)
object_schema(({ "start": index, "end": index } : Json), required=[
"start", "end",
])
}
///|
fn semantics_numeric_schema() -> Json {
let optional_number = nullable_schema(({ "type": "number" } : Json))
object_schema(
(
{
"current": optional_number,
"min": optional_number,
"max": optional_number,
"step": optional_number,
"value_text": nullable_schema(({ "type": "string" } : Json)),
} : Json),
required=["current", "min", "max", "step", "value_text"],
)
}
///|
fn semantics_text_schema() -> Json {
object_schema(
(
{
"selection": nullable_schema(text_range_schema()),
"caret": nullable_schema(({ "type": "integer", "minimum": 0 } : Json)),
"editable": { "type": "boolean" },
} : Json),
required=["selection", "caret", "editable"],
)
}
///|
fn semantics_collection_schema() -> Json {
let optional_integer = nullable_schema(({ "type": "integer" } : Json))
object_schema(
(
{
"row_index": optional_integer,
"row_count": optional_integer,
"row_span": optional_integer,
"column_index": optional_integer,
"column_count": optional_integer,
"column_span": optional_integer,
"set_size": optional_integer,
"set_position": optional_integer,
} : Json),
required=[
"row_index", "row_count", "row_span", "column_index", "column_count", "column_span",
"set_size", "set_position",
],
)
}
///|
fn semantics_relations_schema(node_id : Json) -> Json {
let ids = array_schema(node_id)
object_schema(
(
{
"labelled_by": ids,
"described_by": ids,
"controls": ids,
"error_message": ids,
"active_descendant": ids,
} : Json),
required=[
"labelled_by", "described_by", "controls", "error_message", "active_descendant",
],
)
}
///|
fn semantics_action_kind_schema() -> Json {
(
{
"type": "string",
"enum": [
"activate", "focus", "set_text", "submit", "scroll", "select", "expand",
"collapse", "dismiss", "increment", "decrement", "set_numeric_value", "set_selection",
"show_menu",
],
} : Json)
}
///|
fn semantics_role_schema() -> Json {
(
{
"type": "string",
"enum": [
"none", "text", "link", "heading", "navigation", "main", "button", "checkbox",
"text_field", "list", "list_item", "grid", "scroll_view", "switch", "radio",
"slider", "progress", "tab", "dialog", "tooltip", "menu", "button_group",
"form", "form_field", "tree", "tree_item", "group", "web_view", "image",
"alert", "status", "combo_box", "option", "table", "row", "cell", "separator",
],
} : Json)
}
///|
fn semantics_node_schema() -> Json {
let node_id = decimal_u64_codec(positive=true).schema
let semantic_id = semantic_id_codec().schema
let optional_integer = nullable_schema(({ "type": "integer" } : Json))
object_schema(
(
{
"node_id": node_id,
"semantic_id": nullable_schema(semantic_id),
"role": semantics_role_schema(),
"level": optional_integer,
"url": { "type": "string" },
"label": { "type": "string" },
"value": { "type": "string" },
"description": { "type": "string" },
"state": semantics_state_schema(),
"numeric": nullable_schema(semantics_numeric_schema()),
"text": nullable_schema(semantics_text_schema()),
"collection": nullable_schema(semantics_collection_schema()),
"relations": semantics_relations_schema(node_id),
"live": { "type": "string", "enum": ["off", "polite", "assertive"] },
"atomic": { "type": "boolean" },
"actions": array_schema(semantics_action_kind_schema()),
"focus_order": optional_integer,
"frame": rect_schema(),
"children": array_schema(node_id),
} : Json),
required=[
"node_id", "semantic_id", "role", "level", "url", "label", "value", "description",
"state", "numeric", "text", "collection", "relations", "live", "atomic", "actions",
"focus_order", "frame", "children",
],
)
}
///|
fn semantics_issue_schema() -> Json {
let node_id = decimal_u64_codec(positive=true).schema
let semantic_id = semantic_id_codec().schema
let relation = (
{
"type": "string",
"enum": [
"labelled_by", "described_by", "controls", "error_message", "active_descendant",
],
} : Json)
one_of_schema([
object_schema(
(
{
"kind": { "const": "duplicate_semantic_id" },
"semantic_id": semantic_id,
"nodes": array_schema(node_id),
} : Json),
required=["kind", "semantic_id", "nodes"],
),
object_schema(
(
{
"kind": { "const": "missing_relation_target" },
"source": node_id,
"relation": relation,
"semantic_id": semantic_id,
} : Json),
required=["kind", "source", "relation", "semantic_id"],
),
object_schema(
(
{
"kind": { "const": "ambiguous_relation_target" },
"source": node_id,
"relation": relation,
"semantic_id": semantic_id,
"nodes": array_schema(node_id),
} : Json),
required=["kind", "source", "relation", "semantic_id", "nodes"],
),
])
}
///|
fn semantics_read_value_schema() -> Json {
let generation = decimal_u64_codec().schema
let node_id = decimal_u64_codec(positive=true).schema
let optional_node_id = nullable_schema(node_id)
let nodes = array_schema(semantics_node_schema())
let issues = array_schema(semantics_issue_schema())
let announcements = array_schema(
object_schema(
(
{
"node_id": node_id,
"generation": generation,
"live": { "type": "string", "enum": ["off", "polite", "assertive"] },
"atomic": { "type": "boolean" },
"text": { "type": "string" },
} : Json),
required=["node_id", "generation", "live", "atomic", "text"],
),
)
one_of_schema([
object_schema(
(
{
"kind": { "const": "full" },
"generation": generation,
"root": optional_node_id,
"focused": optional_node_id,
"nodes": nodes,
"issues": issues,
} : Json),
required=["kind", "generation", "root", "focused", "nodes", "issues"],
),
object_schema(
(
{
"kind": { "const": "delta" },
"from": generation,
"to": generation,
"root": optional_node_id,
"focused": optional_node_id,
"upserted": nodes,
"removed": array_schema(node_id),
"issues": issues,
"announcements": announcements,
} : Json),
required=[
"kind", "from", "to", "root", "focused", "upserted", "removed", "issues",
"announcements",
],
),
object_schema(
({ "kind": { "const": "unchanged" }, "generation": generation } : Json),
required=["kind", "generation"],
),
])
}
///|
fn action_receipt_value_schema() -> Json {
let generation = decimal_u64_codec().schema
object_schema(
(
{
"before": generation,
"after": generation,
"pending_work": { "type": "boolean" },
} : Json),
required=["before", "after", "pending_work"],
)
}