Anubis.Server.Frame (anubis_mcp v2.2.0)

Copy Markdown

The Anubis Frame — pure user state + read-only context.

User fields

  • assigns - shared user data as a map. For HTTP transports, this inherits from Plug.Conn.assigns.

Component maps

Runtime-registered components are stored in typed maps keyed by name/URI:

  • tools - %{name => %Tool{}}
  • resources - %{uri => %Resource{}}
  • prompts - %{name => %Prompt{}}
  • resource_templates - %{name => %Resource{uri_template: ...}}

Pagination

  • pagination_limit - optional limit for listing operations

Context

  • context - read-only %Context{}, refreshed by Session before each callback

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.

Returns true if the request carries validated OAuth 2.1 claims.

Returns the OAuth 2.1 claims from the current request context, or nil if no authorization is configured or the transport is STDIO.

Clears all runtime-registered components

Returns the settings the client declared for the extension id under capabilities.extensions, or nil when it did not declare that extension.

Whether the client declared capability ("elicitation", "sampling", "roots", ...) for the current request.

Reconstructs Frame from a previously saved map.

Retrieves all runtime-registered components as a flat list

Returns true if the bearer token grants all of the given scopes.

Returns true if the bearer token grants the given scope.

Returns the client's answer to the input request under key, or nil.

Returns the answers the client sent to an Anubis.Server.InputRequired, keyed as its input requests were. Empty when the request carries none.

Creates a new frame with optional initial assigns.

Returns the progressToken the client sent with the request being served, or nil when it asked for no progress.

Sets the pagination limit for listing operations.

Registers a prompt definition at runtime.

Registers a resource definition with a fixed URI.

Registers a resource template definition using a URI template (RFC 6570).

Registers a tool definition at runtime.

Returns the _meta of the request being served, or an empty map.

Returns the verified state a handler attached with Anubis.Server.InputRequired.state/2, or nil when the request carries none.

Returns whether this session has an active subscription for the given URI.

Returns the list of granted scopes from the bearer token.

Returns the sub (subject) claim from the bearer token, or nil.

Records that this session has subscribed to updates for the given resource URI.

Serializes Frame for persistent storage.

Removes a previously-recorded subscription for the given URI.

Types

server_component_t()

t()

@type t() :: %Anubis.Server.Frame{
  assigns: map(),
  context: Anubis.Server.Context.t(),
  pagination_limit: non_neg_integer() | nil,
  prompts: %{optional(String.t()) => Anubis.Server.Component.Prompt.t()},
  resource_subscriptions: MapSet.t(String.t()),
  resource_templates: %{
    optional(String.t()) => Anubis.Server.Component.Resource.t()
  },
  resources: %{optional(String.t()) => Anubis.Server.Component.Resource.t()},
  task_id: String.t() | nil,
  tools: %{optional(String.t()) => Anubis.Server.Component.Tool.t()}
}

Functions

assign(frame, assigns)

@spec assign(t(), Enumerable.t()) :: t()

Assigns a value or multiple values to the frame.

Examples

frame = Frame.assign(frame, :status, :active)
frame = Frame.assign(frame, %{status: :active, count: 5})
frame = Frame.assign(frame, status: :active, count: 5)

assign(frame, key, value)

@spec assign(t(), key :: atom(), value :: any()) :: t()

assign_new(frame, key, fun)

@spec assign_new(t(), key :: atom(), value_fun :: (-> term())) :: t()

Assigns a value to the frame only if the key doesn't already exist.

The value is computed lazily using the provided function.

Examples

frame = Frame.assign_new(frame, :timestamp, fn -> DateTime.utc_now() end)

authenticated?(frame)

@spec authenticated?(t()) :: boolean()

Returns true if the request carries validated OAuth 2.1 claims.

Examples

Frame.authenticated?(frame)
# => true

authorization(frame)

@spec authorization(t()) :: Anubis.Server.Context.auth_claims() | nil

Returns the OAuth 2.1 claims from the current request context, or nil if no authorization is configured or the transport is STDIO.

Examples

case Frame.authorization(frame) do
  nil -> # no auth configured
  claims -> claims.sub
end

clear_components(frame)

@spec clear_components(t()) :: t()

Clears all runtime-registered components

client_extension(frame, id)

@spec client_extension(t(), String.t()) :: map() | nil

Returns the settings the client declared for the extension id under capabilities.extensions, or nil when it did not declare that extension.

An empty map means the extension is supported with no settings.

Examples

case Frame.client_extension(frame, "io.modelcontextprotocol/ui") do
  %{"mimeTypes" => types} when is_list(types) -> "text/html;profile=mcp-app" in types
  _ -> false
end

client_supports?(frame, capability)

@spec client_supports?(t(), String.t()) :: boolean()

Whether the client declared capability ("elicitation", "sampling", "roots", ...) for the current request.

from_saved(map)

@spec from_saved(map()) :: t()

Reconstructs Frame from a previously saved map.

Restored: assigns, pagination_limit, resource_subscriptions. Runtime-only fields (tools, resources, prompts, resource_templates) are initialized empty — their validator functions are not serializable. context is left as the default struct and will be set by Session before each callback invocation.

get_components(frame)

@spec get_components(t()) :: [server_component_t()]

Retrieves all runtime-registered components as a flat list

has_all_scopes?(frame, required)

@spec has_all_scopes?(t(), [String.t()]) :: boolean()

Returns true if the bearer token grants all of the given scopes.

Examples

Frame.has_all_scopes?(frame, ["tools:read", "tools:write"])
# => true

has_scope?(frame, scope)

@spec has_scope?(t(), String.t()) :: boolean()

Returns true if the bearer token grants the given scope.

Examples

Frame.has_scope?(frame, "tools:read")
# => true

input_response(frame, key)

@spec input_response(t(), String.t()) :: map() | nil

Returns the client's answer to the input request under key, or nil.

Examples

case Frame.input_response(frame, "confirm") do
  %{"action" => "accept", "content" => content} -> content
  _not_answered -> nil
end

input_responses(frame)

@spec input_responses(t()) :: %{required(String.t()) => map()}

Returns the answers the client sent to an Anubis.Server.InputRequired, keyed as its input requests were. Empty when the request carries none.

new(assigns \\ %{})

@spec new(assigns :: map()) :: t()

Creates a new frame with optional initial assigns.

Examples

iex> Frame.new()
%Frame{assigns: %{}}

iex> Frame.new(%{user: "alice"})
%Frame{assigns: %{user: "alice"}}

progress_token(frame)

@spec progress_token(t()) :: String.t() | integer() | nil

Returns the progressToken the client sent with the request being served, or nil when it asked for no progress.

Examples

if token = Frame.progress_token(frame) do
  Anubis.Server.send_progress(token, 50, total: 100)
end

put_pagination_limit(frame, limit)

@spec put_pagination_limit(t(), non_neg_integer()) :: t()

Sets the pagination limit for listing operations.

Examples

frame = Frame.put_pagination_limit(frame, 10)
frame.pagination_limit
# => 10

register_prompt(frame, name, opts)

@spec register_prompt(t(), String.t(), [prompt_opt]) :: t()
when prompt_opt:
       {:description, String.t() | nil}
       | {:arguments, map() | nil}
       | {:title, String.t() | nil}
       | {:scopes, [String.t()]}

Registers a prompt definition at runtime.

register_resource(frame, uri, opts)

@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}
       | {:meta, map() | nil}
       | {:scopes, [String.t()]}

Registers a resource definition with a fixed URI.

For parameterized resources, use register_resource_template/3 instead.

register_resource_template(frame, uri_template, opts)

@spec register_resource_template(t(), String.t(), [resource_template_opt]) :: t()
when resource_template_opt:
       {:title, String.t() | nil}
       | {:name, String.t()}
       | {:description, String.t() | nil}
       | {:mime_type, String.t() | nil}
       | {:meta, map() | nil}
       | {:scopes, [String.t()]}

Registers a resource template definition using a URI template (RFC 6570).

Examples

frame = Frame.register_resource_template(frame, "file:///{path}",
  name: "project_files",
  title: "Project Files",
  description: "Access files in the project directory"
)

register_tool(frame, name, opts)

@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}
       | {:task_support, Anubis.Server.Component.Tool.task_support()}
       | {:scopes, [String.t()]}

Registers a tool definition at runtime.

request_meta(frame)

@spec request_meta(t()) :: map()

Returns the _meta of the request being served, or an empty map.

request_state(frame)

@spec request_state(t()) :: term()

Returns the verified state a handler attached with Anubis.Server.InputRequired.state/2, or nil when the request carries none.

resource_subscribed?(frame, uri)

@spec resource_subscribed?(t(), uri :: String.t()) :: boolean()

Returns whether this session has an active subscription for the given URI.

scopes(frame)

@spec scopes(t()) :: [String.t()]

Returns the list of granted scopes from the bearer token.

Returns an empty list when no authorization is present.

Examples

Frame.scopes(frame)
# => ["tools:read", "tools:write"]

subject(frame)

@spec subject(t()) :: String.t() | nil

Returns the sub (subject) claim from the bearer token, or nil.

Examples

Frame.subject(frame)
# => "user-id-123"

subscribe_resource(frame, uri)

@spec subscribe_resource(t(), uri :: String.t()) :: t()

Records that this session has subscribed to updates for the given resource URI.

Idempotent — subscribing twice to the same URI is a no-op. Per the MCP spec, the URI does not need to refer to a currently-registered resource.

to_saved(frame)

@spec to_saved(t()) :: map()

Serializes Frame for persistent storage.

Only assigns and pagination_limit are persisted. The following fields are runtime-only and excluded from serialization:

  • tools — runtime-registered tool definitions (includes validator functions)
  • resources — runtime-registered resource definitions
  • prompts — runtime-registered prompt definitions
  • resource_templates — runtime-registered resource template definitions
  • context — rebuilt by Session before each callback invocation

Compile-time components (registered via the component macro) are always available from the server module and do not need persistence.

unsubscribe_resource(frame, uri)

@spec unsubscribe_resource(t(), uri :: String.t()) :: t()

Removes a previously-recorded subscription for the given URI.