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
Supported Plug boundary options.
@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()]}
@type scope_policy() :: %{optional(String.t()) => scope_policy_mode()}
Per-method opt-ins to definition-based HTTP authorization.
@type scope_policy_mode() :: :visible_definitions | :selected_definition
An explicit definition-based authorization mode for one MCP method.
Functions
@spec call(Plug.Conn.t(), map()) :: Plug.Conn.t()
Authenticates and serves one HTTP request through the MCP boundary.
@spec init([plug_option()]) :: map()
Initializes a Plug state for an already-supervised server and pinned auth boundary.