MCP.Server.Handler behaviour (MCP Elixir SDK v1.0.1)

Copy Markdown View Source

Behaviour for implementing MCP server feature handlers.

Implement this behaviour to define how your server responds to client requests for tools, resources, prompts, completions, and logging.

All callbacks are optional — the server only advertises capabilities for callbacks that your module actually implements.

Example

defmodule MyHandler do
  @behaviour MCP.Server.Handler

  @impl true
  def init(opts) do
    {:ok, %{tools: Keyword.get(opts, :tools, [])}}
  end

  @impl true
  def handle_list_tools(_cursor, state) do
    {:ok, state.tools, nil, state}
  end

  @impl true
  def handle_call_tool("echo", %{"message" => msg}, state) do
    {:ok, [%{"type" => "text", "text" => msg}], state}
  end
end

Summary

Callbacks

Execute a tool. Called on tools/call.

Execute a tool with context. Called on tools/call when the handler implements this 4-arity version.

Complete an argument value. Called on completion/complete.

Get a specific prompt. Called on prompts/get.

Return a list of prompts. Called on prompts/list.

Return a list of resource templates. Called on resources/templates/list.

Return a list of resources. Called on resources/list.

Return a list of tools. Called on tools/list.

Read a resource by URI. Called on resources/read.

Set the logging level. Called on logging/setLevel.

Subscribe to resource updates. Called on resources/subscribe.

Unsubscribe from resource updates. Called on resources/unsubscribe.

Initialize handler state. Called when the server starts.

Types

cursor()

@type cursor() :: String.t() | nil

state()

@type state() :: term()

Callbacks

handle_call_tool(name, arguments, state)

(optional)
@callback handle_call_tool(name :: String.t(), arguments :: map(), state()) ::
  {:ok, content :: [map()], state()}
  | {:ok, content :: [map()], is_error :: boolean(), state()}
  | {:error, code :: integer(), message :: String.t(), state()}

Execute a tool. Called on tools/call.

handle_call_tool(name, arguments, context, state)

(optional)
@callback handle_call_tool(
  name :: String.t(),
  arguments :: map(),
  context :: MCP.Server.ToolContext.t(),
  state()
) ::
  {:ok, content :: [map()], state()}
  | {:ok, content :: [map()], is_error :: boolean(), state()}
  | {:error, code :: integer(), message :: String.t(), state()}

Execute a tool with context. Called on tools/call when the handler implements this 4-arity version.

The context (MCP.Server.ToolContext) allows sending notifications (logging, progress) and making server-to-client requests (sampling, elicitation) during tool execution.

When this callback is implemented, tool execution runs asynchronously, enabling SSE streaming of intermediate messages.

handle_complete(ref, argument, state)

(optional)
@callback handle_complete(ref :: map(), argument :: map(), state()) ::
  {:ok, completion :: map(), state()}

Complete an argument value. Called on completion/complete.

handle_get_prompt(name, arguments, state)

(optional)
@callback handle_get_prompt(name :: String.t(), arguments :: map() | nil, state()) ::
  {:ok, result :: map(), state()}
  | {:error, code :: integer(), message :: String.t(), state()}

Get a specific prompt. Called on prompts/get.

handle_list_prompts(cursor, state)

(optional)
@callback handle_list_prompts(cursor(), state()) ::
  {:ok, prompts :: [map()], next_cursor :: cursor(), state()}

Return a list of prompts. Called on prompts/list.

handle_list_resource_templates(cursor, state)

(optional)
@callback handle_list_resource_templates(cursor(), state()) ::
  {:ok, templates :: [map()], next_cursor :: cursor(), state()}

Return a list of resource templates. Called on resources/templates/list.

handle_list_resources(cursor, state)

(optional)
@callback handle_list_resources(cursor(), state()) ::
  {:ok, resources :: [map()], next_cursor :: cursor(), state()}

Return a list of resources. Called on resources/list.

handle_list_tools(cursor, state)

(optional)
@callback handle_list_tools(cursor(), state()) ::
  {:ok, tools :: [map()], next_cursor :: cursor(), state()}

Return a list of tools. Called on tools/list.

handle_read_resource(uri, state)

(optional)
@callback handle_read_resource(uri :: String.t(), state()) ::
  {:ok, contents :: [map()], state()}
  | {:error, code :: integer(), message :: String.t(), state()}

Read a resource by URI. Called on resources/read.

handle_set_log_level(level, state)

(optional)
@callback handle_set_log_level(level :: String.t(), state()) :: {:ok, state()}

Set the logging level. Called on logging/setLevel.

handle_subscribe(uri, state)

(optional)
@callback handle_subscribe(uri :: String.t(), state()) ::
  {:ok, state()} | {:error, code :: integer(), message :: String.t(), state()}

Subscribe to resource updates. Called on resources/subscribe.

handle_unsubscribe(uri, state)

(optional)
@callback handle_unsubscribe(uri :: String.t(), state()) ::
  {:ok, state()} | {:error, code :: integer(), message :: String.t(), state()}

Unsubscribe from resource updates. Called on resources/unsubscribe.

init(opts)

@callback init(opts :: keyword()) :: {:ok, state()} | {:error, term()}

Initialize handler state. Called when the server starts.