Anubis.Server.Notifier (anubis_mcp v2.2.0)

Copy Markdown

Sends notifications and server-initiated requests back to the client that owns frame.

Every callback receives a Anubis.Server.Frame, and every frame carries the pid of the Session serving it. These helpers use that pid, so they work from any process: the Session itself, an application process, or a task spawned inside a tool callback. That last case is why they exist. Anubis.Server.send_tools_list_changed/0 and its siblings use send(self(), ...), which is a no-op outside the Session process.

defmodule MyApp.Tools.Reindex do
  use Anubis.Server.Component, type: :tool

  @impl true
  def execute(%{"job" => job}, frame) do
    MyApp.Reindex.run(job, fn step ->
      Notifier.progress(frame, step.token, step.done, total: step.total)
    end)

    {:reply, Response.tool("queued"), frame}
  end
end

Notifications the client may have subscribed to are dropped by the session when it should not receive them (see send_resource_updated/3), exactly like their Anubis.Server counterparts.

See Anubis.Server.Context for session_pid and request_meta, the two fields these helpers read off the frame.

Summary

Types

A frame handed to a server callback.

Functions

Sends notifications/log/message. Requires the client to declare the :logging capability.

Sends an arbitrary notification to the client.

Sends notifications/prompts/list_changed.

Sends notifications/resources/updated for one resource.

Sends notifications/resources/list_changed.

Sends a roots/list request.

Sends a sampling/createMessage request.

Sends a notifications/tasks/status carrying the current state of a task.

Sends notifications/tools/list_changed.

Types

frame()

@type frame() :: Anubis.Server.Frame.t()

A frame handed to a server callback.

Functions

elicitation_request(frame, message, requested_schema, opts \\ [])

@spec elicitation_request(frame(), String.t(), map(), keyword()) ::
  :ok | {:error, term()}

Sends an elicitation/create request.

Asks the user for structured input. requested_schema is validated before anything goes on the wire, and the session rejects the request if the client did not declare the elicitation capability. The response arrives at the server's handle_elicitation/3 callback.

Per the MCP specification, servers MUST NOT use elicitation to request sensitive information (credentials, payment details, anything the user would not type in front of a stranger).

log_message(frame, level, message, data \\ nil)

@spec log_message(frame(), Logger.level(), String.t(), map() | nil) :: :ok

Sends notifications/log/message. Requires the client to declare the :logging capability.

notify(frame, method, params \\ %{})

@spec notify(frame(), String.t(), map()) :: :ok

Sends an arbitrary notification to the client.

Every helper below is a wrapper over this. Use it directly for notifications Anubis has no typed helper for.

Notifier.notify(frame, "notifications/resources/updated", %{"uri" => uri})

progress(frame, progress_token, progress, opts \\ [])

@spec progress(frame(), String.t() | non_neg_integer(), number(), keyword()) :: :ok

Sends notifications/progress.

The token normally comes from the request's own _meta:

Notifier.progress(frame, frame.context.request_meta["progressToken"], 50, total: 100)

prompts_list_changed(frame)

@spec prompts_list_changed(frame()) :: :ok

Sends notifications/prompts/list_changed.

resource_updated(frame, uri, timestamp \\ nil)

@spec resource_updated(frame(), String.t(), DateTime.t() | nil) :: :ok

Sends notifications/resources/updated for one resource.

Subscription-gated: the session drops it unless the client previously sent resources/subscribe for this URI.

resources_list_changed(frame)

@spec resources_list_changed(frame()) :: :ok

Sends notifications/resources/list_changed.

roots_request(frame, opts \\ [])

@spec roots_request(frame(), keyword()) :: :ok

Sends a roots/list request.

The response arrives at the server's handle_roots/3 callback.

sampling_request(frame, messages, opts \\ [])

@spec sampling_request(frame(), [map()], keyword()) :: :ok

Sends a sampling/createMessage request.

The response arrives at the server's handle_sampling/3 callback.

task_status(frame, task_id)

@spec task_status(frame(), String.t()) :: :ok

Sends a notifications/tasks/status carrying the current state of a task.

Looked up in the server's task store. Per spec (2025-11-25) receivers MAY send these; requestors MUST NOT rely on them.

tools_list_changed(frame)

@spec tools_list_changed(frame()) :: :ok

Sends notifications/tools/list_changed.