Skip to main content

sim_lib_mcp/
schema.rs

1use sim_kernel::{Cx, Expr, Result, ShapeRef, Symbol};
2
3/// Converts a SIM `shape` into a JSON-Schema [`Expr`] map for MCP clients.
4///
5/// A missing shape maps to the permissive empty schema; core scalar shapes map
6/// to their JSON-Schema `type`, and other shapes are carried through an
7/// `x-sim-shape` extension key.
8pub fn shape_to_json_schema(cx: &mut Cx, shape: Option<&ShapeRef>) -> Result<Expr> {
9    let Some(shape) = shape else {
10        return Ok(any_schema());
11    };
12    let expr = shape.object().as_expr(cx)?;
13    Ok(schema_from_shape_expr(&expr))
14}
15
16fn schema_from_shape_expr(expr: &Expr) -> Expr {
17    match expr {
18        Expr::Symbol(symbol) if symbol == &Symbol::qualified("core", "Any") => any_schema(),
19        Expr::Symbol(symbol) if symbol == &Symbol::qualified("core", "String") => {
20            typed_schema("string")
21        }
22        Expr::Symbol(symbol) if symbol == &Symbol::qualified("core", "Number") => {
23            typed_schema("number")
24        }
25        Expr::Symbol(symbol) if symbol == &Symbol::qualified("core", "Bool") => {
26            typed_schema("boolean")
27        }
28        Expr::Symbol(symbol) if symbol == &Symbol::qualified("core", "Nil") => typed_schema("null"),
29        Expr::Symbol(symbol) => schema_with_sim_shape(symbol.to_string()),
30        _ => schema_with_sim_shape(format!("{expr:?}")),
31    }
32}
33
34fn any_schema() -> Expr {
35    Expr::Map(Vec::new())
36}
37
38fn typed_schema(kind: &str) -> Expr {
39    Expr::Map(vec![field("type", Expr::String(kind.to_owned()))])
40}
41
42fn schema_with_sim_shape(shape: String) -> Expr {
43    Expr::Map(vec![field("x-sim-shape", Expr::String(shape))])
44}
45
46use sim_value::build::entry as field;