Sake.Schema (Sake v0.4.4)

Copy Markdown View Source

Builds JSON Schema 2020-12 from arg declarations and validates arguments against it.

The validator covers what the declarations emit: object properties, required, additionalProperties, the primitive types, enum, arrays of one type, minimum/maximum, minLength/maxLength, pattern, and default. It never fetches a $ref.

Summary

Functions

Every x-mcp-header reachable through properties only, as {name, path} pairs. Unlike header_paths/1, a name used twice appears twice, so a check for duplicates sees it.

Header name → property path for every x-mcp-header reachable through properties only.

Validates input (string or atom keys) against schema. Returns the arguments with atom keys for declared properties (undeclared properties keep their string keys when the schema allows them, so client input never creates atoms), defaults applied, and nil for absent optional properties.

Types

arg()

@type arg() :: {atom(), type(), keyword()}

type()

@type type() ::
  :string
  | :integer
  | :number
  | :boolean
  | :any
  | {:array, type()}
  | {:enum, [term()]}
  | {:object, [arg()]}

Functions

build(args)

@spec build([arg()]) :: map()

header_entries(schema)

@spec header_entries(map()) :: [{String.t(), [String.t()]}]

Every x-mcp-header reachable through properties only, as {name, path} pairs. Unlike header_paths/1, a name used twice appears twice, so a check for duplicates sees it.

header_paths(schema)

@spec header_paths(map()) :: %{required(String.t()) => [String.t()]}

Header name → property path for every x-mcp-header reachable through properties only.

valid_header_name?(name)

@spec valid_header_name?(term()) :: boolean()

validate(schema, input, opts \\ [])

@spec validate(map(), map(), keyword()) ::
  {:ok, map()} | {:error, [%{path: [String.t()], message: String.t()}]}

Validates input (string or atom keys) against schema. Returns the arguments with atom keys for declared properties (undeclared properties keep their string keys when the schema allows them, so client input never creates atoms), defaults applied, and nil for absent optional properties.

With absent: :omit, an absent optional property with no default is left out instead, at every level (objects in arrays too): the arguments are what the client sent, and a tool tells a property the client left out from one it sent as null.