Anubis.Server.Frame (anubis_mcp v0.14.0)
The Anubis Frame.
This module defines a struct and functions for working with MCP server state throughout the request/response lifecycle.
User fields
These fields contain user-controlled data:
assigns
- shared user data as a map. For HTTP transports, this inherits fromPlug.Conn.assigns
. Users are responsible for populating authentication data through their Plug pipeline before it reaches the MCP server.
Transport fields
These fields contain transport-specific context. The structure varies by transport type:
HTTP transport (when transport.type == :http
)
req_headers
- the request headers as a list, example:[{"content-type", "application/json"}]
. All header names are downcased.query_params
- the request query params as a map, example:%{"session" => "abc123"}
. Returnsnil
if query params were not fetched by the Plug pipeline.remote_ip
- the IP of the client, example:{151, 236, 219, 228}
. This field is set by the transport layer.scheme
- the request scheme as an atom, example::https
host
- the requested host as a binary, example:"api.example.com"
port
- the requested port as an integer, example:443
request_path
- the requested path, example:"/mcp"
STDIO transport (when transport.type == :stdio
)
env
- environment variables as a map, example:%{"USER" => "alice", "HOME" => "/home/alice"}
pid
- the OS process ID as a string, example:"12345"
MCP protocol fields
These fields contain MCP-specific data:
request
- the current MCP request being processed, with fields:id
- the request ID for correlationmethod
- the MCP method being called, example:"tools/call"
params
- the raw request parameters (before validation)
initialized
- boolean indicating if the MCP session has been initialized
Private fields
These fields are reserved for framework usage:
private
- shared framework data as a map. Contains MCP session context:session_id
- unique identifier for the current client session being handledclient_info
- client information from initialization, example:%{"name" => "my-client", "version" => "1.0.0"}
client_capabilities
- negotiated client capabilitiesprotocol_version
- active MCP protocol version, example:"2025-03-26"
Summary
Functions
Assigns a value or multiple values to the frame.
Assigns a value to the frame only if the key doesn't already exist.
Clears all current registered components (tools, resources, prompts)
Clears the current request from the frame.
Clears all session-specific private data from the frame.
Gets the client capabilities from the frame's private data.
Gets the client info from the frame's private data.
Retrieves all current registered components (tools, resources, prompts)
Gets the MCP session ID from the frame's private data.
Gets the protocol version from the frame's private data.
Gets a query parameter value from HTTP transport.
Gets a request header value from HTTP transport.
Creates a new frame with optional initial assigns.
Sets the pagination limit for listing operations.
Sets or updates private session data in the frame.
Sets the current request being processed.
Sets or updates transport data in the frame.
Registers a prompt definition.
Registers a resource definition. THis also supports resource templates (via URI templates).
Registers a tool definition.
Types
@type private_t() :: %{ optional(:session_id) => String.t(), optional(:client_info) => map(), optional(:client_capabilities) => map(), optional(:protocol_version) => String.t(), optional(:server_module) => module(), optional(:server_registry) => module(), optional(:pagination_limit) => non_neg_integer(), optional(:__mcp_components__) => [server_component_t()] }
@type server_component_t() :: Anubis.Server.Component.Tool.t() | Anubis.Server.Component.Resource.t() | Anubis.Server.Component.Prompt.t()
@type stdio_t() :: %{type: :stdio, os_pid: non_neg_integer(), env: map()}
@type t() :: %Anubis.Server.Frame{ assigns: Enumerable.t(), initialized: boolean(), private: private_t(), request: request_t() | nil, transport: transport_t() }
Functions
@spec assign(t(), Enumerable.t()) :: t()
Assigns a value or multiple values to the frame.
Examples
# Single assignment
frame = Frame.assign(frame, :status, :active)
# Multiple assignments via map
frame = Frame.assign(frame, %{status: :active, count: 5})
# Multiple assignments via keyword list
frame = Frame.assign(frame, status: :active, count: 5)
Assigns a value to the frame only if the key doesn't already exist.
The value is computed lazily using the provided function, which is only called if the key is not present in assigns.
Examples
# Only assigns if :timestamp doesn't exist
frame = Frame.assign_new(frame, :timestamp, fn -> DateTime.utc_now() end)
# Function is not called if key exists
frame = frame |> Frame.assign(:count, 5)
|> Frame.assign_new(:count, fn -> expensive_computation() end)
# count remains 5
Clears all current registered components (tools, resources, prompts)
Clears the current request from the frame.
This should be called after processing a request to ensure the frame doesn't retain stale request data.
Examples
frame = Frame.clear_request(frame)
Clears all session-specific private data from the frame.
This should be called when a session ends to ensure the frame doesn't retain stale session data.
Examples
frame = Frame.clear_session(frame)
Gets the client capabilities from the frame's private data.
Examples
capabilities = Frame.get_client_capabilities(frame)
# => %{"tools" => %{}, "resources" => %{}}
Gets the client info from the frame's private data.
Examples
client_info = Frame.get_client_info(frame)
# => %{"name" => "my-client", "version" => "1.0.0"}
@spec get_components(t()) :: [server_component_t()]
Retrieves all current registered components (tools, resources, prompts)
Gets the MCP session ID from the frame's private data.
Examples
session_id = Frame.get_mcp_session_id(frame)
# => "session_abc123"
Gets the protocol version from the frame's private data.
Examples
version = Frame.get_protocol_version(frame)
# => "2025-03-26"
Gets a query parameter value from HTTP transport.
Returns the parameter value, or nil if the transport is not HTTP, query params weren't fetched, or the parameter doesn't exist.
Examples
# HTTP transport with query params
session = Frame.get_query_param(frame, "session")
# => "abc123"
# Missing parameter or non-HTTP transport
missing = Frame.get_query_param(frame, "nonexistent")
# => nil
Gets a request header value from HTTP transport.
Returns the first value for the header, or nil if the transport is not HTTP or the header is not present.
Examples
# HTTP transport
auth_header = Frame.get_req_header(frame, "authorization")
# => "Bearer token123"
# Non-HTTP transport or missing header
auth_header = Frame.get_req_header(frame, "authorization")
# => nil
@spec new(assigns :: Enumerable.t()) :: t()
Creates a new frame with optional initial assigns.
Examples
iex> Frame.new()
%Frame{assigns: %{}, initialized: false}
iex> Frame.new(%{user: "alice"})
%Frame{assigns: %{user: "alice"}, initialized: false}
@spec put_pagination_limit(t(), non_neg_integer()) :: t()
Sets the pagination limit for listing operations.
This limit is used by handlers when returning lists of tools, prompts, or resources
to control the maximum number of items returned in a single response. When the limit
is set and the total number of items exceeds it, the response will include a
nextCursor
field for pagination.
Examples
# Set pagination limit to 10 items per page
frame = Frame.put_pagination_limit(frame, 10)
# The limit is stored in private data
frame.private.pagination_limit
# => 10
@spec put_private(t(), Enumerable.t()) :: t()
Sets or updates private session data in the frame.
Private data is used for framework-internal session context that persists across requests, similar to Plug.Conn.private.
Examples
# Set single private value
frame = Frame.put_private(frame, :session_id, "abc123")
# Set multiple private values
frame = Frame.put_private(frame, %{
session_id: "abc123",
client_info: %{name: "my-client", version: "1.0.0"}
})
Sets the current request being processed.
The request includes the request ID, method, and raw parameters before validation.
Examples
frame = Frame.put_request(frame, %{
id: "req_123",
method: "tools/call",
params: %{"name" => "calculator", "arguments" => %{}}
})
@spec put_transport(t(), Enumerable.t()) :: t()
Sets or updates transport data in the frame.
Check transport_t()
for reference.
Examples
# Set single transport value
frame = Frame.put_transport(frame, :session_id, "abc123")
# Set multiple transport values
frame = Frame.put_transport(frame, %{
session_id: "abc123",
client_info: %{name: "my-client", version: "1.0.0"}
})
@spec register_prompt(t(), String.t(), [prompt_opt]) :: t() when prompt_opt: {:description, String.t() | nil} | {:arguments, map() | nil} | {:title, String.t() | nil}
Registers a prompt definition.
@spec register_resource(t(), String.t(), [resource_opt]) :: t() when resource_opt: {:title, String.t() | nil} | {:name, String.t() | nil} | {:description, String.t() | nil} | {:mime_type, String.t() | nil}
Registers a resource definition. THis also supports resource templates (via URI templates).
@spec register_tool(t(), String.t(), [tool_opt]) :: t() when tool_opt: {:description, String.t() | nil} | {:input_schema, map() | nil} | {:output_schema, map() | nil} | {:title, String.t() | nil} | {:annotations, map() | nil}
Registers a tool definition.