barrel_mcp_ctx (barrel_mcp v3.0.1)
View SourcePer-request context.
MCP 2026-07-28 made the protocol stateless: a request carries its own protocol version, client capabilities and identity in _meta instead of inheriting them from an initialize handshake. This module builds one context per request from the decoded JSON-RPC envelope and answers questions about it, so nothing else in the library has to reach into _meta by hand.
Every request belongs to one of two eras:
modern:params._metacarriesio.modelcontextprotocol/protocolVersion. Stateless, no session.legacy: everything else, including everyinitialize. Version and capabilities live on the session and are supplied by the transport throughExtra.
Building a context never fails, so a malformed request still yields something the caller can inspect. Use validate/1 to check that a modern request carries the _meta fields the spec requires.
Summary
Functions
The authenticated principal, as returned by the auth provider.
Capabilities the client declared for this request.
The client's self-reported name and version, or undefined. Self-reported and unverified: use it for display and logging, never for security decisions.
The elicitation modes this client declared. "An empty capabilities object is equivalent to declaring support for form mode only" (2026-07-28/client/elicitation.mdx:67).
Which era this request belongs to.
Equivalent to from_request(Request, #{}).
Build the context for one decoded JSON-RPC request or notification. Never fails.
One MRTR response by key, raw as it arrived.
Every response the client supplied on an MRTR retry, keyed by the identifiers the server assigned in inputRequests.
Whether this request uses per-request metadata (2026-07-28+).
Minimum log level the server should emit for this request, or undefined. When it is undefined the server must emit no notifications/message at all for this request.
The raw params._meta map, for callers that need a key this module does not expose (progress tokens, trace context, extensions).
The stable identity behind the credential, as barrel_mcp_auth:authenticate/3 derived it.
Protocol version for this request. undefined for a legacy request whose session has not recorded one yet.
The opaque MRTR state the client echoed back, still signed. Callers verify it through barrel_mcp_request_state rather than reading it here.
Session this request belongs to, or undefined. Always undefined for a modern request: the era has no sessions.
Whether the transport can hold a response stream open.
Whether the client declared a core capability, e.g. elicitation, sampling or roots. A server must not ask for something the client did not declare.
Whether the client declared an extension, by identifier (e.g. ?MCP_EXT_TASKS).
Check that a modern request carries the _meta fields the spec marks required, and that the optional ones it does carry are usable. A failure is malformed params and the caller must reject it with ?JSONRPC_INVALID_PARAMS (HTTP 400).
Check the stated revision on its own, before anything is judged against it. A peer naming a revision we do not speak may legitimately carry a _meta shape we would misjudge, so the version error has to win; but a version that is absent or not a string is a _meta problem, and answering "unsupported version: undefined" names the wrong one.
Types
-type ctx() :: #{era := era(), protocol_version := binary() | undefined, client_info := map() | undefined, client_capabilities := map(), log_level := binary() | undefined, input_responses := map(), request_state := binary() | undefined, session_id := binary() | undefined, auth_info := term(), streaming := boolean(), meta := map()}.
-type era() :: modern | legacy.
Functions
The authenticated principal, as returned by the auth provider.
Capabilities the client declared for this request.
The client's self-reported name and version, or undefined. Self-reported and unverified: use it for display and logging, never for security decisions.
The elicitation modes this client declared. "An empty capabilities object is equivalent to declaring support for form mode only" (2026-07-28/client/elicitation.mdx:67).
Which era this request belongs to.
Equivalent to from_request(Request, #{}).
Build the context for one decoded JSON-RPC request or notification. Never fails.
One MRTR response by key, raw as it arrived.
Every response the client supplied on an MRTR retry, keyed by the identifiers the server assigned in inputRequests.
Whether this request uses per-request metadata (2026-07-28+).
Minimum log level the server should emit for this request, or undefined. When it is undefined the server must emit no notifications/message at all for this request.
The raw params._meta map, for callers that need a key this module does not expose (progress tokens, trace context, extensions).
The stable identity behind the credential, as barrel_mcp_auth:authenticate/3 derived it.
This is what owns a task, a sealed request state or an elicitation, never the whole auth_info() map: that carries exp and jti, so a refreshed token would read as a different caller and orphan whatever was started under the old one.
anonymous when the request carried no credential at all.
Protocol version for this request. undefined for a legacy request whose session has not recorded one yet.
The opaque MRTR state the client echoed back, still signed. Callers verify it through barrel_mcp_request_state rather than reading it here.
Session this request belongs to, or undefined. Always undefined for a modern request: the era has no sessions.
Whether the transport can hold a response stream open.
false unless the transport says otherwise, because most cannot: stdio has one output channel and the plain HTTP transport answers once. Only the Streamable HTTP engine sets it. A method that needs a long-lived response is method-not-found without it, rather than something the transport is handed and cannot serve.
Whether the client declared a core capability, e.g. elicitation, sampling or roots. A server must not ask for something the client did not declare.
Whether the client declared an extension, by identifier (e.g. ?MCP_EXT_TASKS).
-spec validate(ctx()) -> ok | {error, {missing_meta, binary()}} | {error, {invalid_meta, binary()}}.
Check that a modern request carries the _meta fields the spec marks required, and that the optional ones it does carry are usable. A failure is malformed params and the caller must reject it with ?JSONRPC_INVALID_PARAMS (HTTP 400).
Legacy requests carry no such requirement and always pass.
-spec validate_version(ctx()) -> ok | {error, {missing_meta, binary()}} | {error, {invalid_meta, binary()}}.
Check the stated revision on its own, before anything is judged against it. A peer naming a revision we do not speak may legitimately carry a _meta shape we would misjudge, so the version error has to win; but a version that is absent or not a string is a _meta problem, and answering "unsupported version: undefined" names the wrong one.