# `Sake.Schema`
[🔗](https://github.com/hattoriai/sake/blob/v0.4.4/lib/sake/schema.ex#L1)

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`.

# `arg`

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

# `type`

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

# `build`

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

# `header_entries`

```elixir
@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`

```elixir
@spec header_paths(map()) :: %{required(String.t()) =&gt; [String.t()]}
```

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

# `valid_header_name?`

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

# `validate`

```elixir
@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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
