AttestoMCP.Server.Plug (attesto_mcp_server v0.14.0)

Copy Markdown View Source

Plug-compatible Streamable HTTP boundary for modern and legacy MCP.

Every protected MCP leg authenticates through AttestoMCP.Plug.ProtectResource before package-owned request decoding or registry dispatch. A host parser placed earlier in the endpoint runs before this Plug and must apply its own input limit. Metadata discovery is the one intentionally public route defined by RFC 9728. Hosts may select request-scoped tool streams with stream_tools or stream_all_tools; both options are validated during init/1.

:auth accepts either a keyword list or a zero-arity remote callback/MFA returning one. Resolver-backed authentication is evaluated for every applicable MCP or metadata request, so runtime authorization configuration is not captured by router compilation. The trusted resolver may supply the canonical resource or origin at runtime; it is checked against the configured Plug path and reused for metadata and audience verification. A resolver may not replace the boundary's canonical assign keys, and failures fail the request closed.

Summary

Types

Supported Plug boundary options.

Per-method opt-ins to definition-based HTTP authorization.

An explicit definition-based authorization mode for one MCP method.

Functions

Authenticates and serves one HTTP request through the MCP boundary.

Initializes a Plug state for an already-supervised server and pinned auth boundary.

Types

auth_options_resolver()

@type auth_options_resolver() ::
  (-> keyword()) | {module(), atom()} | {module(), atom(), [term()]}

Supported Plug boundary options.

plug_option()

@type plug_option() ::
  {:server, pid() | atom()}
  | {:path, String.t()}
  | {:auth, keyword() | auth_options_resolver()}
  | {:scope_map, map()}
  | {:scope_policy, scope_policy()}
  | {:default_scopes, [String.t()]}
  | {:scopes_supported, [String.t()]}
  | {:context_builder,
     (Plug.Conn.t() -> map())
     | {module(), atom()}
     | {module(), atom(), [term()]}}
  | {:subscription_scopes, [String.t()]}
  | {:max_body_bytes, pos_integer()}
  | {:max_message_bytes, pos_integer()}
  | {:allow_dynamic_origin, boolean()}
  | {:origin, String.t()}
  | {:base_url, String.t()}
  | {:resource, String.t()}
  | {:resource_audience, String.t()}
  | {:stream_keepalive_ms, pos_integer()}
  | {:legacy_keepalive_ms, pos_integer()}
  | {:subscription_timeout, pos_integer()}
  | {:stream_queue_size, pos_integer()}
  | {:subscription_queue_size, pos_integer()}
  | {:max_queue, pos_integer()}
  | {:stream_all_tools, boolean()}
  | {:stream_tools, [String.t()]}

scope_policy()

@type scope_policy() :: %{optional(String.t()) => scope_policy_mode()}

Per-method opt-ins to definition-based HTTP authorization.

scope_policy_mode()

@type scope_policy_mode() :: :visible_definitions | :selected_definition

An explicit definition-based authorization mode for one MCP method.

Functions

call(conn, state)

@spec call(Plug.Conn.t(), map()) :: Plug.Conn.t()

Authenticates and serves one HTTP request through the MCP boundary.

init(opts)

@spec init([plug_option()]) :: map()

Initializes a Plug state for an already-supervised server and pinned auth boundary.