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

Runtime parameter validation using NimbleOptions.

This module provides runtime parameter validation for MCP tools,
prompts, and resources using NimbleOptions schemas. It works alongside
the existing JSON Schema generation to provide both client-side
validation (JSON Schema) and server-side validation (NimbleOptions).

## Features

- Runtime parameter validation with detailed error messages
- Type coercion (string "123" -> integer 123)
- Advanced constraints (min/max, length limits)
- Custom validator functions
- Compile-time schema generation for performance

## Configuration

Validation behavior can be configured in your application config:

    config :conduit_mcp, :validation,
      runtime_validation: true,           # Enable/disable validation
      strict_mode: true,                  # Fail on errors vs. log and continue
      type_coercion: true,                # Automatic type conversion
      log_validation_errors: false        # Log validation failures

A param can override `type_coercion` for itself with its own
`type_coercion: true | false` option; see `ConduitMcp.Validation.SchemaConverter`.

# `format_validation_errors`

```elixir
@spec format_validation_errors([map()] | :tool_not_found | :prompt_not_found) :: [
  map()
]
```

Formats validation errors into a standardized format for JSON-RPC responses.

Converts atom-keyed error maps to the string-keyed form used in MCP error
responses. The error lists `validate_tool_params/3` and
`validate_prompt_args/3` return are already in that form, and formatting
them again returns them unchanged.

Also accepts the bare `:tool_not_found` / `:prompt_not_found` atoms those
functions can return, so a caller matching `{:error, errs}` and formatting
`errs` cannot raise `FunctionClauseError` on a name typo.

## Examples

    iex> errors = [%{parameter: "age", value: -5, message: "must be >= 0"}]
    iex> ConduitMcp.Validation.format_validation_errors(errors)
    [%{"parameter" => "age", "value" => -5, "message" => "must be >= 0"}]

    iex> ConduitMcp.Validation.format_validation_errors(:tool_not_found)
    [%{"parameter" => nil, "value" => nil, "message" => "Unknown tool"}]

# `update_validation_config`

Updates the validation configuration at runtime.

Writes to both Application env and persistent_term so that
the change is visible immediately to all concurrent readers.

# `validate_prompt_args`

```elixir
@spec validate_prompt_args(module(), String.t(), map()) ::
  {:ok, map()} | {:error, [map()] | :prompt_not_found}
```

Validates prompt arguments using the compiled NimbleOptions schema.

Same return contract as `validate_tool_params/3`, with
`{:error, :prompt_not_found}` in place of `{:error, :tool_not_found}`.

# `validate_tool_params`

```elixir
@spec validate_tool_params(module(), String.t(), map()) ::
  {:ok, map()} | {:error, [map()] | :tool_not_found}
```

Validates tool parameters using the compiled NimbleOptions schema.

Returns one of:

  * `{:ok, validated_params}` — valid, with types coerced and defaults
    applied. Also returned verbatim when the server declares no validation
    schema, or when runtime validation is disabled.
  * `{:error, validation_errors}` — a **list** of error maps with string
    keys `"parameter"`, `"value"` and `"message"`, ready to place in a
    JSON-RPC error's `data`.
  * `{:error, :tool_not_found}` — a bare **atom**: the server has validation
    schemas but none for this tool. Propagated rather than folded into the
    error list so the handler can answer "Unknown tool" instead of burying it
    in a "Parameter validation failed" payload's `data.errors`.

## Examples

    iex> ConduitMcp.Validation.validate_tool_params(MyServer, "greet", %{"name" => "Alice"})
    {:ok, %{"name" => "Alice"}}

    iex> ConduitMcp.Validation.validate_tool_params(MyServer, "calc", %{"age" => "-5"})
    {:error, [%{"parameter" => "age", "value" => -5, "message" => "must be greater than or equal to 0"}]}

    iex> ConduitMcp.Validation.validate_tool_params(MyServer, "nope", %{})
    {:error, :tool_not_found}

---

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