# `ConduitMcp.Validation.SchemaConverter`
[🔗](https://github.com/nyo16/conduit_mcp/blob/v0.10.1/lib/conduit_mcp/validation/schema_converter.ex#L1)

Converts DSL parameter definitions to NimbleOptions validation schemas.

This module takes parameter definitions from the ConduitMCP DSL and
converts them into NimbleOptions schemas for runtime validation.
It handles type mapping, constraint extraction, and validation rule
compilation.

## Type Mapping

DSL types are mapped to NimbleOptions types as follows:

- `:string` -> `:string`
- `:integer` -> `:integer`
- `:number` -> `:number` (float)
- `:boolean` -> `:boolean`
- `:object` -> `{:map, :any, :any}` for an open object (no declared fields);
  `:map` with a nested `keys:` schema when nested fields are declared
- `:array` -> `{:list, type}` where type is the item type
- `{:array, item_type}` -> `{:list, converted_item_type}`

## Validation Options

DSL options are converted to NimbleOptions validation rules:

- `required: true` -> `required: true`
- `enum: [...]` -> `in: [...]`
- `default: value` -> `default: value`
- `min: value` -> `min: value`
- `max: value` -> `max: value`
- `min_length: value` -> `min_length: value`
- `max_length: value` -> `max_length: value`
- `validator: function` -> `validator: function`

## Nested Objects

An `:object` param that declares nested fields is converted to
`type: :map, keys: [...]`, built by recursing through this same function, so
nesting works to any depth and NimbleOptions reports errors with a key path
(`in options [:bag, :inner]`).

Undeclared keys in such an object are rejected unless the param carries
`additional_properties: true`. An object with no declared fields is an open
bag: `{:map, :any, :any}`, anything accepted.

Nested keys arrive as strings over JSON-RPC and NimbleOptions' nested
validation is atom-key-only, so `ConduitMcp.Validation` atomises nested keys
before validating — but only those matching a *declared* field name, which
are already interned at compile time. Client input never mints an atom.

### Enforcement boundary

Nested validation covers `:object` params and objects nested inside them.
It does **not** cover objects inside `:array` items: NimbleOptions has no
way to attach a `keys:` schema to a list element type. Item schemas declared
with `items :object do ... end` are published in the JSON Schema for clients
but are not enforced server-side beyond `{:list, :any}`.

# `compile_validation_schema`

Compiles a complete tool definition to a NimbleOptions validation schema.

Takes a tool definition with parameters and converts it to a schema
that can be used for runtime validation.

# `dsl_params_to_nimble_options`

Converts a list of DSL parameter definitions to a NimbleOptions schema.

## Examples

    iex> params = [
    ...>   %{name: :name, type: :string, opts: [required: true]},
    ...>   %{name: :age, type: :integer, opts: [min: 0, max: 150]}
    ...> ]
    iex> ConduitMcp.Validation.SchemaConverter.dsl_params_to_nimble_options(params)
    [
      name: [type: :string, required: true],
      age: [type: :integer, min: 0, max: 150]
    ]

# `format_detailed_errors`

Enhanced error formatter for NimbleOptions validation errors.

Takes a NimbleOptions.ValidationError and converts it to detailed
error information suitable for MCP responses.

# `strip_markers`

Strips custom constraint markers from a schema, recursing through nested
`keys:` schemas.

The markers carry constraints NimbleOptions has no native option for
(`enum`, `min`/`max`, length limits, custom validators) plus the
`additional_properties` knob. They ride alongside the real options in the
*full* schema and must be removed before it reaches NimbleOptions, which
rejects unknown option keys — including inside a nested `keys:` schema.

Single source of truth. Used by `ConduitMcp.Validation`,
`ConduitMcp.DSL.SchemaBuilder`, and `ConduitMcp.Endpoint`.

# `validate_schema`

Validates a NimbleOptions schema definition.

Checks if the generated schema is valid for NimbleOptions.
Used during compile time to catch schema generation errors.

---

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