# `Mead.MCP.Server.Configuration`
[🔗](https://github.com/albertosca/mead/blob/main/lib/mead/mcp/server/configuration.ex#L1)

Reads and validates `config :mead, :mcp_server`.

Pure: it starts nothing and reads no application environment itself; `Mead.MCP.Server` hands
it the settings. Without a `:token` the server is `:disabled`. Any other problem is an error
whose message names the setting and never contains the token or any other value.

The struct keeps a SHA-256 digest of the token, not the token, and its `Inspect` omits even
the digest, so the listener options that carry the struct leak nothing into a crash report or
`:sys.get_status/1`. Hashing both sides before comparing also gives `secure_compare/2` two
binaries of equal length whatever the client sent.

## Settings

  * `:token` - the bearer token clients must present (a non-empty string; required to enable
    the server).
  * `:port` - `0..65535`; defaults to 4040. Tests use 0 and ask the listener for its port.
  * `:address` - an IPv4 tuple; defaults to loopback `{127, 0, 0, 1}`.
  * `:allow_effect` - whether `tools: "effect"` is honoured; defaults to `false`.
  * `:allowed_origins` - exact `Origin` values accepted besides loopback; defaults to `[]`.
  * `:maximum_connections` - how many connections the listener serves at once, rounded up to a
    multiple of 10 from 10 on; a connection over the limit is closed after about a second
    without room. A positive integer; defaults to 250.
  * `:certfile` and `:keyfile` - paths to a PEM certificate chain and its private key. Both
    together make the listener speak HTTPS (and WSS for the deck); either alone is an error;
    without them it speaks plain HTTP. The files are not read here, only the shape is checked,
    so a missing file stops the listener at boot with the TLS library's own message.

# `t`

```elixir
@type t() :: %Mead.MCP.Server.Configuration{
  address: :inet.ip4_address(),
  allow_effect: boolean(),
  allowed_origins: [String.t()],
  maximum_connections: pos_integer(),
  port: 0..65535,
  token_digest: binary()
}
```

# `listener`

```elixir
@spec listener(term()) ::
  {:ok,
   %{
     port: 0..65535,
     address: :inet.ip4_address(),
     allowed_origins: [String.t()],
     maximum_connections: pos_integer(),
     tls: nil | [certfile: Path.t(), keyfile: Path.t()]
   }}
  | {:error, String.t()}
```

Reads only the listener settings (`:port`, `:address`, `:allowed_origins`,
`:maximum_connections`), which are valid with or without a token, so the shared listener can
start for the deck alone. The deck applies the same `Origin` rule as MCP.

# `load`

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

# `token_matches?`

```elixir
@spec token_matches?(t(), term()) :: boolean()
```

Whether `presented` is the configured token, compared in constant time.

---

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