barrel_mcp_http_engine (barrel_mcp v2.0.2)

View Source

Transport-neutral MCP HTTP engine.

Holds the protocol logic for both the simple HTTP transport and the Streamable HTTP transport (POST/GET/DELETE/OPTIONS, SSE, sessions, CORS, Origin validation, authentication, the OAuth protected-resource-metadata endpoint and async tool calls) WITHOUT any dependency on a concrete HTTP server.

A binding (the built-in barrel_mcp_http_listener h1/h2 server, or an external adapter such as Livery's) reads the request line and body, then calls handle/6 with:

  • Method — the request method binary (<<"POST">> …).
  • Path — the request target (query string allowed; it is stripped here).
  • Headers — a [{binary(), binary()}] proplist. Lookups are case-insensitive.
  • Body — the full request body (<<>> when none).
  • Responder — a map of I/O closures (see below).
  • Config — the engine configuration (see the config() type).

The Responder abstracts response delivery so the engine never touches a socket:

   #{reply        => fun((Status, Headers, Body) -> ok),
     stream_start => fun((Status, Headers) -> ok),
     stream_chunk => fun((iodata()) -> ok | {error, term()}),
     stream_end   => fun(() -> ok)}

Headers passed to the closures is a [{binary(), binary()}] proplist (lowercase names). A streaming (SSE) response is stream_start then repeated stream_chunk then stream_end.

handle/6 runs in the calling (per-request) process. For a long-lived GET SSE stream it blocks in a receive loop until the session is terminated or the binding signals a client disconnect by sending the calling process the message mcp_disconnect.

Summary

Types

config/0

-type config() ::
          #{mode := stream | simple,
            auth_config := map(),
            session_enabled => boolean(),
            allowed_origins => any | [term()],
            allow_missing_origin => boolean(),
            sse_buffer_size => pos_integer(),
            resource_metadata => undefined | map(),
            _ => _}.

responder/0

-type responder() ::
          #{reply := fun((non_neg_integer(), [{binary(), binary()}], iodata()) -> ok),
            stream_start := fun((non_neg_integer(), [{binary(), binary()}]) -> ok),
            stream_chunk := fun((iodata()) -> ok | {error, term()}),
            stream_end := fun(() -> ok)}.

Functions

ensure_session_manager()

handle(Method, RawPath, Headers, Body, Responder, Config)

-spec handle(binary(), binary(), [{binary(), binary()}], binary(), responder(), config()) -> ok.

init_auth(AuthOpts)

inject_resource_metadata_url(AuthConfig, _)

is_loopback(_)

normalize_resource_metadata(M)

resolve_allowed_origins(Loopback, List)