# `Anubis.Server.McpParam`

Tool parameters mirrored into `Mcp-Param-{Name}` HTTP headers, which the
2026-07-28 Streamable HTTP transport defines.

A tool opts a parameter in with an `x-mcp-header` property in its
`inputSchema`; with the component DSL, `field :region, :string,
mcp_header: "Region"`. A client then sends the argument's value in the
`Mcp-Param-Region` header, and the server rejects the call with `-32020`
(`HeaderMismatch`, HTTP 400) when a header disagrees with the body, is
missing for a value the body carries, or holds characters a header value
cannot.

An annotation must name a header token, unique regardless of case, on a
string, integer or boolean parameter reachable from the schema root through
`properties` alone. A schema that breaks any of these raises
`ArgumentError` when its JSON Schema is built.

Headers mirror wire arguments before defaults or transformations. A routing
selector that must be visible to intermediaries should be required and keep
the same meaning during validation; authorization remains the application's
responsibility. See the [transport guide](transports.md#tool-parameters-in-headers).

# `annotate`

```elixir
@spec annotate(map(), map()) :: map()
```

Puts the `x-mcp-header` annotations of a component schema into the JSON
Schema built from it.

`schema` is the expanded component schema, whose fields carry `mcp_header:`
in their metadata; `json_schema` is what it was encoded to. Raises
`ArgumentError` for an annotation the specification does not allow.

# `decode_header_value`

```elixir
@spec decode_header_value(String.t()) :: {:ok, String.t()} | :error
```

Decodes a header value that may carry the `=?base64?…?=` sentinel.

Spaces and tabs around the value are not part of it (RFC 9110, section 5.5)
and are dropped first. A value without the sentinel, or with only its
prefix, is returned as written. A sentinel around invalid Base64 is `:error`.

## Examples

    iex> Anubis.Server.McpParam.decode_header_value("us-west1")
    {:ok, "us-west1"}

    iex> Anubis.Server.McpParam.decode_header_value("=?base64?SGVsbG8=?=")
    {:ok, "Hello"}

    iex> Anubis.Server.McpParam.decode_header_value("=?base64?SGVsbG8?=")
    :error

    iex> Anubis.Server.McpParam.decode_header_value("  test_tool 	")
    {:ok, "test_tool"}

# `encode_header_value`

```elixir
@spec encode_header_value(String.t()) :: String.t()
```

Encodes a value for a `Mcp-Name`-style header, the inverse of
`decode_header_value/1`.

A value that is already a safe header value (visible ASCII only, no leading
or trailing space or tab, not starting with the base64 sentinel) is returned
as written; anything else is wrapped in the `=?base64?...?=` sentinel so the
server decodes it back to the exact body value.

## Examples

    iex> Anubis.Server.McpParam.encode_header_value("us-west1")
    "us-west1"

    iex> Anubis.Server.McpParam.encode_header_value("Héllo")
    "=?base64?" <> Base.encode64("Héllo") <> "?="

    iex> Anubis.Server.McpParam.decode_header_value(Anubis.Server.McpParam.encode_header_value(" padded "))
    {:ok, " padded "}

# `validate`

```elixir
@spec validate(map() | nil, map() | nil, %{required(String.t()) =&gt; String.t()}) ::
  :ok | {:error, Anubis.MCP.Error.t()}
```

Checks the `Mcp-Param-*` headers of a `tools/call` against its arguments.

`input_schema` is the tool's JSON Schema, `arguments` the call's arguments
and `headers` the request's headers, with lowercase names. Arguments are the
values received on the wire, before server defaults or transformations.
Absent or null values require no header. Returns `:ok` when they match,
otherwise `{:error, error}` with protocol code `-32020`.

Mirrored integers must be within the IEEE 754 safe range, ±(2^53 - 1).

---

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