Mead.MCP.Server.Configuration (mead v0.1.1)

Copy Markdown View Source

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.

Summary

Functions

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.

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

Types

t()

@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()
}

Functions

listener(settings)

@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(settings)

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

token_matches?(configuration, presented)

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

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