ExMCP.Server.Handler behaviour (ex_mcp v0.1.0)

View Source

Behaviour for implementing MCP server handlers.

To create an MCP server, implement this behaviour in your module:

defmodule MyServer do
  use ExMCP.Server.Handler

  @impl true
  def handle_initialize(_params, state) do
    {:ok, %{
      server_info: %{
        name: "my-server",
        version: "1.0.0"
      },
      capabilities: %{
        tools: %{},
        resources: %{list: true, read: true}
      }
    }, state}
  end

  @impl true
  def handle_list_tools(state) do
    tools = [
      %{
        name: "hello",
        description: "Says hello",
        input_schema: %{
          type: "object",
          properties: %{
            "name" => %{type: "string"}
          },
          required: ["name"]
        }
      }
    ]
    {:ok, tools, state}
  end

  @impl true
  def handle_call_tool("hello", %{"name" => name}, state) do
    result = [%{type: "text", text: "Hello, #{name}!"}]
    {:ok, result, state}
  end

  # ... implement other handlers ...
end

The use macro provides default implementations for all callbacks that return appropriate "not implemented" responses.

Summary

Callbacks

Handles a completion request.

Handles getting a prompt.

Handles the initialize request from a client.

Handles listing available prompts.

Handles listing available resources.

Handles listing available tools.

Handles reading a resource.

Called when the handler process is started.

Called when the handler process is terminating.

Types

initialize_result()

@type initialize_result() :: ExMCP.Types.initialize_result()

prompt()

@type prompt() :: ExMCP.Types.prompt()

resource()

@type resource() :: ExMCP.Types.resource()

state()

@type state() :: any()

tool()

@type tool() :: ExMCP.Types.tool()

Callbacks

handle_call_tool(name, arguments, state)

@callback handle_call_tool(name :: String.t(), arguments :: map(), state()) ::
  {:ok, ExMCP.Types.tool_result(), state()} | {:error, any(), state()}

Handles a tool call.

handle_complete(ref, params, state)

(optional)
@callback handle_complete(ref :: String.t(), params :: map(), state()) ::
  {:ok, result :: map(), state()} | {:error, any(), state()}

Handles a completion request.

handle_get_prompt(name, arguments, state)

(optional)
@callback handle_get_prompt(name :: String.t(), arguments :: map(), state()) ::
  {:ok, ExMCP.Types.prompt_message(), state()} | {:error, any(), state()}

Handles getting a prompt.

handle_initialize(params, state)

@callback handle_initialize(params :: map(), state()) ::
  {:ok, initialize_result(), state()} | {:error, any(), state()}

Handles the initialize request from a client.

Should return server information and capabilities.

handle_list_prompts(state)

(optional)
@callback handle_list_prompts(state()) ::
  {:ok, [prompt()], state()} | {:error, any(), state()}

Handles listing available prompts.

handle_list_resources(state)

(optional)
@callback handle_list_resources(state()) ::
  {:ok, [resource()], state()} | {:error, any(), state()}

Handles listing available resources.

handle_list_tools(state)

@callback handle_list_tools(state()) ::
  {:ok, [tool()], state()} | {:error, any(), state()}

Handles listing available tools.

handle_read_resource(uri, state)

(optional)
@callback handle_read_resource(uri :: String.t(), state()) ::
  {:ok, ExMCP.Types.resource_content(), state()} | {:error, any(), state()}

Handles reading a resource.

init(args)

@callback init(args :: any()) :: {:ok, state()} | {:error, any()}

Called when the handler process is started.

terminate(reason, state)

(optional)
@callback terminate(reason :: any(), state()) :: any()

Called when the handler process is terminating.