Anubis.Client (anubis_mcp v2.2.0)

Copy Markdown

MCP (Model Context Protocol) client for connecting to MCP servers.

This module provides a fully functional MCP client with automatic supervision, transport management, and all standard MCP operations. No macros needed — just add it to your supervision tree with the desired configuration.

Usage

Add the client to your supervision tree:

children = [
  {Anubis.Client,
   name: MyApp.MCPClient,
   transport: {:stdio, command: "uvx", args: ["mcp-server-anthropic"]},
   client_info: %{"name" => "MyApp", "version" => "1.0.0"},
   capabilities: %{"roots" => %{}},
   protocol_version: "2025-06-18"}
]

Use the client by passing the registered name:

{:ok, tools} = Anubis.Client.list_tools(MyApp.MCPClient)
{:ok, result} = Anubis.Client.call_tool(MyApp.MCPClient, "search", %{query: "elixir"})

Capabilities

Capabilities are passed as a map with string keys:

%{"roots" => %{}, "sampling" => %{}}

For convenience, use parse_capability/2 to build from atoms:

capabilities =
  [:sampling, {:roots, list_changed?: true}]
  |> Enum.reduce(%{}, &Anubis.Client.parse_capability/2)

Transport Configuration

When starting the client, provide transport configuration:

  • {:stdio, command: "cmd", args: ["arg1", "arg2"]}
  • {:streamable_http, url: "http://localhost:8000/mcp"}

Process Naming

The :name option controls process registration. You can use any valid GenServer.name() — an atom, a PID, or a {:via, module, term} tuple:

# Atom name
{Anubis.Client, name: MyApp.MCPClient, transport: ...}

# For distributed systems with registries (e.g., Horde)
{Anubis.Client,
 name: {:via, Horde.Registry, {MyCluster, "client_1"}},
 transport_name: {:via, Horde.Registry, {MyCluster, "transport_1"}},
 transport: ...}

When using via tuples or other non-atom names, you must explicitly provide the :transport_name option. For atom names, the transport is automatically named as Module.concat(ClientName, "Transport").

Dynamic Client Management

For applications that need to manage multiple client connections dynamically (e.g., user-configured MCP servers), use a DynamicSupervisor:

DynamicSupervisor.start_child(
  MyApp.DynamicSupervisor,
  {Anubis.Client,
   name: {:via, Registry, {MyApp.Registry, client_id}},
   transport_name: {:via, Registry, {MyApp.Registry, {client_id, :transport}}},
   transport: {:streamable_http, base_url: url},
   client_info: %{"name" => "MyApp", "version" => "1.0.0"},
   capabilities: %{},
   protocol_version: "2025-06-18"}
)

Summary

Types

MCP client capabilities

MCP client metadata info

Elicitation callback function type.

Elicitation completion callback function type.

The connection era the client is in.

Log callback function type.

MCP client initialization options

Progress callback function type.

Root directory specification.

t()

MCP client transport options

URL-mode elicitation callback function type.

Functions

Adds a root directory to the client's roots list.

Blocks until the client has completed the MCP initialization handshake.

Returns a child specification for starting the client under a supervisor.

Clears all root directories.

Closes the client connection and terminates the process.

Requests autocompletion suggestions for prompt arguments or resource URIs.

Returns the connection era the client is in.

Gets a specific prompt from the server.

Gets the server's capabilities as reported during initialization.

Gets the server's information as reported during initialization.

Guard to check if an atom is a valid client capability.

Guard to check if a capability is supported by checking map keys.

Lists available prompts from the server.

Lists available resource templates from the server.

Lists available resources from the server.

Gets a list of all root directories.

Lists available tools from the server.

Merges additional capabilities into the client's capabilities.

Converts a capability atom or tuple into a map entry.

Sends a ping request to the server to check connection health. Returns :pong if successful.

Reads a specific resource from the server.

Registers a callback function to handle elicitation requests from the server.

Registers a callback for notifications/elicitation/complete.

Registers a callback function to be called when log messages are received.

Registers a callback function to be called when progress notifications are received for the specified progress token.

Registers a callback function to handle sampling requests from the server.

Registers a callback function to handle URL-mode elicitation requests.

Removes a root directory from the client's roots list.

Sends a progress notification to the server for a long-running operation.

Sets the minimum log level for the server to send log messages.

Starts the client supervision tree (client + transport).

Subscribes to updates for a specific resource URI.

Unregisters the elicitation callback.

Unregisters the notifications/elicitation/complete callback.

Unregisters a previously registered log callback.

Unregisters a previously registered progress callback for the specified token.

Unregisters the sampling callback.

Unregisters the URL-mode elicitation callback.

Unsubscribes from updates for a previously-subscribed resource URI.

Types

capabilities()

@type capabilities() :: %{
  optional(:roots | String.t()) => %{
    optional(:listChanged | String.t()) => boolean()
  },
  optional(:sampling | String.t()) => %{},
  optional(:elicitation | String.t()) => %{}
}

MCP client capabilities

  • :roots - Capabilities related to the roots resource
    • :listChanged - Whether the client can handle listChanged notifications
  • :sampling - Capabilities related to sampling
  • :elicitation - Capabilities related to elicitation (server-initiated user input requests, 2025-06-18)

MCP describes these client capabilities on its specification

capabilities_input()

@type capabilities_input() :: [
  capability() | {capability(), capability_opts()} | map()
]

capability()

@type capability() :: :roots | :sampling | :elicitation

capability_opts()

@type capability_opts() :: [{:list_changed?, boolean()}]

client_info()

@type client_info() :: %{
  required(:name | String.t()) => String.t(),
  optional(:version | String.t()) => String.t()
}

MCP client metadata info

  • :name - The name of the client (required)
  • :version - The version of the client

elicitation_callback()

@type elicitation_callback() :: (message :: String.t(), requested_schema :: map() ->
                             {:accept, map()}
                             | :decline
                             | :cancel
                             | {:error, String.t()})

Elicitation callback function type.

Called when the server sends an elicitation/create request. The callback receives the human-readable message and the requestedSchema (a restricted JSON Schema subset). It must return one of:

  • {:accept, content} — user submitted content (a flat map matching the schema)
  • :decline — user explicitly declined
  • :cancel — user dismissed without an explicit choice
  • {:error, reason} — internal error; sent back as a JSON-RPC error

elicitation_complete_callback()

@type elicitation_complete_callback() :: (elicitation_id :: String.t() -> any())

Elicitation completion callback function type.

Called with the elicitation_id when the server reports that a URL-mode elicitation finished out of band. Delivery is not guaranteed, so this is an affordance, not something to block on.

era()

@type era() :: :detecting | :connecting | :legacy | :stateless

The connection era the client is in.

  • :detecting - started, nothing sent yet
  • :connecting - a server/discover probe or an initialize is in flight
  • :legacy - the initialize handshake completed
  • :stateless - the probe answered in the stateless era

log_callback()

@type log_callback() :: (level :: String.t(),
                   data :: term(),
                   logger :: String.t() | nil ->
                     any())

Log callback function type.

Called when log message notifications are received from the server.

Parameters

  • level - Log level as a string (e.g., "debug", "info", "warning", "error")
  • data - Log message data, typically a map with message details
  • logger - Optional logger name identifying the source

Returns

  • The return value is ignored

option()

@type option() ::
  {:name, GenServer.name()}
  | {:transport, transport()}
  | {:client_info, map()}
  | {:capabilities, map()}
  | {:protocol_version, String.t()}
  | GenServer.option()

MCP client initialization options

  • :name - Following the GenServer patterns described on "Name registration".
  • :transport - The MCP transport options
  • :client_info - Information about the client
  • :capabilities - Client capabilities to advertise to the MCP server
  • :protocol_version - Protocol version to use (defaults to the latest supported version)

Any other option support by GenServer.

progress_callback()

@type progress_callback() :: (progress_token :: String.t() | integer(),
                        progress :: number(),
                        total :: number() | nil ->
                          any())

Progress callback function type.

Called when progress notifications are received for a specific progress token.

Parameters

  • progress_token - String or integer identifier for the progress operation
  • progress - Current progress value
  • total - Total expected value (nil if unknown)

Returns

  • The return value is ignored

root()

@type root() :: %{uri: String.t(), name: String.t() | nil}

Root directory specification.

Represents a root directory that the client has access to.

Fields

  • :uri - File URI for the root directory (e.g., "file:///home/user/project")
  • :name - Optional human-readable name for the root

t()

@type t() :: GenServer.server()

transport()

@type transport() :: [
  layer: Anubis.Transport.STDIO | Anubis.Transport.StreamableHTTP,
  name: GenServer.server()
]

MCP client transport options

url_elicitation_callback()

@type url_elicitation_callback() :: (url :: String.t(),
                               elicitation_id :: String.t(),
                               message :: String.t() ->
                                 :accept
                                 | :decline
                                 | :cancel
                                 | {:error, String.t()})

URL-mode elicitation callback function type.

Called when the server sends an elicitation/create with mode: "url". The callback receives the url the user should open, the opaque elicitation_id, and the message explaining why. It must return one of:

  • :accept — user consented to the out-of-band interaction
  • :decline — user explicitly declined
  • :cancel — user dismissed without an explicit choice
  • {:error, reason} — internal error; sent back as a JSON-RPC error

An out-of-band response never carries content: the interaction happens outside the client, which only learns the outcome from a notifications/elicitation/complete notification.

Functions

add_root(client, uri, name \\ nil, opts \\ [])

@spec add_root(t(), String.t(), String.t() | nil, opts :: Keyword.t()) :: :ok

Adds a root directory to the client's roots list.

Parameters

  • client - The client process
  • uri - The URI of the root directory (must start with "file://")
  • name - Optional human-readable name for the root
  • opts - Additional options
    • :timeout - Request timeout in milliseconds

await_ready(client, opts \\ [])

@spec await_ready(t(), keyword()) :: :ok | {:error, Anubis.MCP.Error.t()}

Blocks until the client has completed the MCP initialization handshake.

Returns :ok once the server capabilities have been received. If the server has already been initialized, returns immediately. Otherwise, the caller is parked until the initialization response arrives or the GenServer call times out. Initialization failures return {:error, error}. An HTTP authorization challenge is preserved in error.data.original_reason as {:authorization_required, challenge} for Anubis.Client.Authorization.discover/2.

Options

  • :timeout - Maximum time to wait in milliseconds (default: 30s)

Examples

{:ok, _supervisor} = Anubis.Client.start_link(opts)
:ok = Anubis.Client.await_ready(MyApp.MCPClient, timeout: 10_000)
{:ok, tools} = Anubis.Client.list_tools(MyApp.MCPClient)

call_tool(client, name, arguments \\ nil, opts \\ [])

@spec call_tool(t(), String.t(), map() | nil, keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Calls a tool on the server.

Options

  • :timeout - Request timeout in milliseconds
  • :progress - Progress tracking options
    • :token - A unique token to track progress (string or integer)
    • :callback - A function to call when progress updates are received

cancel_all_requests(client, reason \\ "client_cancelled", opts \\ [])

@spec cancel_all_requests(t(), String.t(), opts :: Keyword.t()) ::
  {:ok, [Anubis.Client.Request.t()]} | {:error, Anubis.MCP.Error.t()}

Cancels all pending requests.

Parameters

  • client - The client process
  • reason - Optional reason for cancellation (defaults to "client_cancelled")

Returns

  • {:ok, requests} - A list of the Request structs that were cancelled
  • {:error, reason} - If an error occurred

cancel_request(client, request_id, reason \\ "client_cancelled", opts \\ [])

@spec cancel_request(t(), String.t(), String.t(), opts :: Keyword.t()) ::
  :ok | {:error, Anubis.MCP.Error.t()}

Cancels an in-progress request.

Parameters

  • client - The client process
  • request_id - The ID of the request to cancel
  • reason - Optional reason for cancellation

Returns

  • :ok if the cancellation was successful
  • {:error, reason} if an error occurred
  • {:not_found, request_id} if the request ID was not found

child_spec(init_arg)

Returns a child specification for starting the client under a supervisor.

This starts a supervision tree containing both the client GenServer and the configured transport process, linked with a :one_for_all strategy.

clear_roots(client, opts \\ [])

@spec clear_roots(t(), opts :: Keyword.t()) :: :ok

Clears all root directories.

Parameters

  • client - The client process
  • opts - Additional options
    • :timeout - Request timeout in milliseconds

close(client)

@spec close(t()) :: :ok

Closes the client connection and terminates the process.

complete(client, ref, argument, opts \\ [])

@spec complete(t(), map(), map(), keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Requests autocompletion suggestions for prompt arguments or resource URIs.

Parameters

  • client - The client process
  • ref - Reference to what is being completed (required)
    • For prompts: %{"type" => "ref/prompt", "name" => prompt_name}
    • For resources: %{"type" => "ref/resource", "uri" => resource_uri}
  • argument - The argument being completed (required)
    • %{"name" => arg_name, "value" => current_value}
  • opts - Additional options
    • :timeout - Request timeout in milliseconds
    • :progress - Progress tracking options
      • :token - A unique token to track progress (string or integer)
      • :callback - A function to call when progress updates are received

Returns

Returns {:ok, response} with completion suggestions if successful, or {:error, reason} if an error occurs.

The response result contains a "completion" object with:

  • values - List of completion suggestions (maximum 100)
  • total - Optional total number of matching items
  • hasMore - Boolean indicating if more results are available

get_era(client, opts \\ [])

@spec get_era(t(), opts :: Keyword.t()) :: era()

Returns the connection era the client is in.

  • :detecting - the client started and has sent nothing yet
  • :connecting - a server/discover probe or an initialize handshake is in flight
  • :legacy - the client talks to the server through the initialize handshake
  • :stateless - the server answered the probe in the stateless era: no session exists, and every request carries its _meta

Options

  • :timeout - Call timeout in milliseconds (default: 5s)

get_prompt(client, name, arguments \\ nil, opts \\ [])

@spec get_prompt(t(), String.t(), map() | nil, keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Gets a specific prompt from the server.

Options

  • :timeout - Request timeout in milliseconds
  • :progress - Progress tracking options
    • :token - A unique token to track progress (string or integer)
    • :callback - A function to call when progress updates are received

get_schema(atom)

get_server_capabilities(client, opts \\ [])

@spec get_server_capabilities(t(), opts :: Keyword.t()) :: map() | nil

Gets the server's capabilities as reported during initialization.

Returns nil if the client has not been initialized yet.

get_server_info(client, opts \\ [])

@spec get_server_info(t(), opts :: Keyword.t()) :: map() | nil

Gets the server's information as reported during initialization.

Returns nil if the client has not been initialized yet.

is_client_capability(capability)

(macro)

Guard to check if an atom is a valid client capability.

is_supported_capability(capabilities, capability)

(macro)

Guard to check if a capability is supported by checking map keys.

list_prompts(client, opts \\ [])

@spec list_prompts(t(), keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Lists available prompts from the server.

Options

  • :cursor - Pagination cursor for continuing a previous request
  • :timeout - Request timeout in milliseconds
  • :progress - Progress tracking options
    • :token - A unique token to track progress (string or integer)
    • :callback - A function to call when progress updates are received

list_resource_templates(client, opts \\ [])

@spec list_resource_templates(t(), keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Lists available resource templates from the server.

Options

  • :cursor - Pagination cursor for continuing a previous request
  • :timeout - Request timeout in milliseconds
  • :progress - Progress tracking options
    • :token - A unique token to track progress (string or integer)
    • :callback - A function to call when progress updates are received

list_resources(client, opts \\ [])

@spec list_resources(t(), keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Lists available resources from the server.

Options

  • :cursor - Pagination cursor for continuing a previous request
  • :timeout - Request timeout in milliseconds
  • :progress - Progress tracking options
    • :token - A unique token to track progress (string or integer)
    • :callback - A function to call when progress updates are received

list_roots(client, opts \\ [])

@spec list_roots(t(), opts :: Keyword.t()) :: [map()]

Gets a list of all root directories.

Parameters

  • client - The client process
  • opts - Additional options
    • :timeout - Request timeout in milliseconds

list_tools(client, opts \\ [])

@spec list_tools(t(), keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Lists available tools from the server.

Options

  • :cursor - Pagination cursor for continuing a previous request
  • :timeout - Request timeout in milliseconds
  • :progress - Progress tracking options
    • :token - A unique token to track progress (string or integer)
    • :callback - A function to call when progress updates are received

merge_capabilities(client, additional_capabilities, opts \\ [])

@spec merge_capabilities(t(), map(), opts :: Keyword.t()) :: map()

Merges additional capabilities into the client's capabilities.

parse_capability(capability, capabilities)

@spec parse_capability(capability() | {capability(), capability_opts()}, map()) ::
  map()

Converts a capability atom or tuple into a map entry.

Useful for building capability maps from ergonomic shorthand:

capabilities =
  [:sampling, {:roots, list_changed?: true}]
  |> Enum.reduce(%{}, &Anubis.Client.parse_capability/2)
# => %{"roots" => %{"listChanged" => true}, "sampling" => %{}}

:list_changed? is only valid for :roots: it is the single client capability carrying a listChanged flag in the MCP schema. Passing it with any other capability raises.

parse_options(data)

parse_options!(data)

ping(client, opts \\ [])

@spec ping(t(), keyword()) :: :pong | {:error, Anubis.MCP.Error.t()}

Sends a ping request to the server to check connection health. Returns :pong if successful.

Unlike the other operations, a successful ping returns the bare atom :pong rather than {:ok, %Anubis.MCP.Response{}}: the MCP ping result is empty, so there is no payload to carry.

The stateless era (2026-07-28) removed ping; against a server detected as stateless this returns {:error, %Anubis.MCP.Error{}} with reason :method_not_found and era: :stateless in the error data.

Options

  • :timeout - Request timeout in milliseconds (default: 30s)
  • :progress - Progress tracking options
    • :token - A unique token to track progress (string or integer)
    • :callback - A function to call when progress updates are received

read_resource(client, uri, opts \\ [])

@spec read_resource(t(), String.t(), keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Reads a specific resource from the server.

Options

  • :timeout - Request timeout in milliseconds
  • :progress - Progress tracking options
    • :token - A unique token to track progress (string or integer)
    • :callback - A function to call when progress updates are received

register_elicitation_callback(client, callback)

@spec register_elicitation_callback(t(), elicitation_callback()) :: :ok

Registers a callback function to handle elicitation requests from the server.

The client must advertise the elicitation capability during initialization for servers to send elicitation/create requests.

Per the MCP specification, the client SHOULD present the request to the user with clear UI, allow them to review and modify their response, and provide decline/cancel options.

register_elicitation_complete_callback(client, callback)

@spec register_elicitation_complete_callback(t(), elicitation_complete_callback()) ::
  :ok

Registers a callback for notifications/elicitation/complete.

register_log_callback(client, callback, opts \\ [])

@spec register_log_callback(t(), log_callback(), opts :: Keyword.t()) :: :ok

Registers a callback function to be called when log messages are received.

Parameters

  • client - The client process
  • callback - A function that takes three arguments: level, data, and logger name

The callback function will be called whenever a log message notification is received.

register_progress_callback(client, progress_token, callback, opts \\ [])

@spec register_progress_callback(
  t(),
  String.t() | integer(),
  progress_callback(),
  opts :: Keyword.t()
) ::
  :ok

Registers a callback function to be called when progress notifications are received for the specified progress token.

Parameters

  • client - The client process
  • progress_token - The progress token to watch for (string or integer)
  • callback - A function that takes three arguments: progress_token, progress, and total

The callback function will be called whenever a progress notification with the matching token is received.

register_sampling_callback(client, callback)

@spec register_sampling_callback(t(), (map() -> {:ok, map()} | {:error, String.t()})) ::
  :ok

Registers a callback function to handle sampling requests from the server.

The callback function will be called when the server sends a sampling/createMessage request. The callback should implement user approval and return the LLM response.

Callback Function

The callback receives the sampling parameters and must return:

  • {:ok, response_map} - Where response_map contains:
    • "role" - Usually "assistant"
    • "content" - Message content (text, image, or audio)
    • "model" - The model that was used
    • "stopReason" - Why generation stopped (e.g., "endTurn")
  • {:error, reason} - If the user rejects or an error occurs

register_url_elicitation_callback(client, callback)

@spec register_url_elicitation_callback(t(), url_elicitation_callback()) :: :ok

Registers a callback function to handle URL-mode elicitation requests.

Separate from the form-mode callback because the arguments and the possible answers differ: URL mode has no requestedSchema and never returns content.

The client must advertise the elicitation capability with the url mode (%{"elicitation" => %{"url" => %{}}}) for servers to send these requests.

remove_root(client, uri, opts \\ [])

@spec remove_root(t(), String.t(), opts :: Keyword.t()) :: :ok

Removes a root directory from the client's roots list.

Parameters

  • client - The client process
  • uri - The URI of the root directory to remove
  • opts - Additional options
    • :timeout - Request timeout in milliseconds

request_opts(data)

request_opts!(data)

send_progress(client, progress_token, progress, total \\ nil, opts \\ [])

@spec send_progress(
  t(),
  String.t() | integer(),
  number(),
  number() | nil,
  opts :: Keyword.t()
) ::
  :ok | {:error, term()}

Sends a progress notification to the server for a long-running operation.

Parameters

  • client - The client process
  • progress_token - The progress token provided in the original request (string or integer)
  • progress - The current progress value (number)
  • total - The optional total value for the operation (number)

Returns :ok if notification was sent successfully, or {:error, reason} otherwise.

set_log_level(client, level)

@spec set_log_level(t(), String.t()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Sets the minimum log level for the server to send log messages.

Parameters

  • client - The client process
  • level - The minimum log level (debug, info, notice, warning, error, critical, alert, emergency)

Returns {:ok, result} if successful, {:error, reason} otherwise.

The stateless era (2026-07-28) removed logging/setLevel; against a server detected as stateless this returns {:error, %Anubis.MCP.Error{}} with reason :method_not_found and era: :stateless in the error data.

start_link(opts)

@spec start_link(keyword()) :: Supervisor.on_start()

Starts the client supervision tree (client + transport).

This is the primary entry point for starting a client. It creates a supervisor that manages both the client GenServer and the transport process.

subscribe_resource(client, uri, opts \\ [])

@spec subscribe_resource(t(), String.t(), keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Subscribes to updates for a specific resource URI.

After a successful subscribe, the server may send notifications/resources/updated notifications for this URI. The server must declare the resources.subscribe capability for this method to succeed.

The stateless era (2026-07-28) removed resources/subscribe; against a server detected as stateless this returns {:error, %Anubis.MCP.Error{}} with reason :method_not_found and era: :stateless in the error data.

Options

  • :timeout - Request timeout in milliseconds

unregister_elicitation_callback(client)

@spec unregister_elicitation_callback(t()) :: :ok

Unregisters the elicitation callback.

unregister_elicitation_complete_callback(client)

@spec unregister_elicitation_complete_callback(t()) :: :ok

Unregisters the notifications/elicitation/complete callback.

unregister_log_callback(client, opts \\ [])

@spec unregister_log_callback(t(), opts :: Keyword.t()) :: :ok

Unregisters a previously registered log callback.

Parameters

  • client - The client process
  • callback - The callback function to unregister

unregister_progress_callback(client, progress_token, opts \\ [])

@spec unregister_progress_callback(t(), String.t() | integer(), opts :: Keyword.t()) ::
  :ok

Unregisters a previously registered progress callback for the specified token.

Parameters

  • client - The client process
  • progress_token - The progress token to stop watching (string or integer)

unregister_sampling_callback(client)

@spec unregister_sampling_callback(t()) :: :ok

Unregisters the sampling callback.

unregister_url_elicitation_callback(client)

@spec unregister_url_elicitation_callback(t()) :: :ok

Unregisters the URL-mode elicitation callback.

unsubscribe_resource(client, uri, opts \\ [])

@spec unsubscribe_resource(t(), String.t(), keyword()) ::
  {:ok, Anubis.MCP.Response.t()} | {:error, Anubis.MCP.Error.t()}

Unsubscribes from updates for a previously-subscribed resource URI.

The stateless era (2026-07-28) removed resources/unsubscribe; against a server detected as stateless this returns {:error, %Anubis.MCP.Error{}} with reason :method_not_found and era: :stateless in the error data.

Options

  • :timeout - Request timeout in milliseconds