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

Validates and normalizes the MCP servers configured under `config :mead, :mcp, servers: [...]`.

    config :mead, :mcp,
      servers: [
        filesystem: [
          transport: {:stdio, command: "npx", arguments: ["-y", "@modelcontextprotocol/server-filesystem", "/data"]},
          tools: [read_text_file: :read, write_file: :effect]
        ],
        remote: [
          transport: {:http, url: "https://example.com/mcp", headers: [{"authorization", "Bearer ..."}]},
          tools: [search: :read]
        ]
      ]

The class of a tool (`:read` or `:effect`) is declared here and nowhere else; the hub never
trusts what a server says about itself. A tool the server offers that is not listed here does
not exist for the model. A server name may hold letters, digits, and hyphens but no underscore,
so the first `__` of `server__tool` always splits the model-facing name.

Validation is strict on purpose: an unknown option is almost always a typo, and a typo in a
permission list silently denies or, worse, mis-grants. Reasons name keys, never values, so a
header carrying a credential cannot reach a log through an error.

A listed tool must also be one the model can be shown: `server__tool` may hold letters, digits,
underscores, and hyphens, and be at most 64 characters. A name outside that would be hidden
without a word, so it is refused here.

# `server`

```elixir
@type server() :: %{
  name: atom(),
  transport: transport(),
  tools: %{required(String.t()) =&gt; :read | :effect}
}
```

# `transport`

```elixir
@type transport() ::
  {:stdio, command: String.t(), arguments: [String.t()]}
  | {:http, url: String.t(), headers: [{String.t(), String.t()}]}
```

# `validate`

```elixir
@spec validate(term()) :: {:ok, [server()]} | {:error, term()}
```

---

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