ExMCP.Server behaviour (ex_mcp v0.9.0)
View SourceHigh-level server implementation with DSL support.
This module provides the use ExMCP.Server macro that enables the
developer-friendly DSL for defining tools, resources, and prompts.
It automatically handles capability registration and provides
sensible defaults for MCP server behavior.
Usage
defmodule MyServer do
use ExMCP.Server
deftool "hello" do
meta do
name "Hello Tool"
description "Says hello to someone"
end
input_schema %{
type: "object",
properties: %{name: %{type: "string"}},
required: ["name"]
}
end
defresource "config://app" do
meta do
name "App Config"
description "Application configuration"
end
mime_type "application/json"
end
defprompt "greeting" do
meta do
name "Greeting Template"
description "A greeting template"
end
arguments do
arg :style, description: "Greeting style"
end
end
# Handler callbacks
@impl true
def handle_tool_call("hello", %{"name" => name}, state) do
{:ok, %{content: [text("Hello, #{name}!")]}, state}
end
@impl true
def handle_resource_read("config://app", _uri, state) do
content = json(%{debug: true, port: 8080})
{:ok, content, state}
end
@impl true
def handle_prompt_get("greeting", args, state) do
style = Map.get(args, "style", "friendly")
messages = [
user("Please greet me in a #{style} way")
]
{:ok, %{messages: messages}, state}
end
end
Summary
Callbacks
Callback for handling initialization requests.
Callback for handling prompt requests.
Callback for listing prompts.
Callback for custom request handling.
Callback for listing resources.
Callback for handling resource reads.
Callback for resource subscriptions.
Callback for resource unsubscriptions.
Callback for handling tool calls.
Functions
Sends a cancellation notification to the server.
Sends a createMessage request to the connected client.
Gets the list of pending request IDs on the server.
Lists the roots available from the connected client.
Sends a progress notification to the client.
Notifies subscribed clients that the prompts list has changed.
Sends a resource update notification for subscribed clients.
Alias for notify_resource_update/2 for backward compatibility.
Notifies subscribed clients that the resource list has changed.
Notifies the client that the server's available roots have changed.
Notifies subscribed clients that the tools list has changed.
Sends a ping request to the client.
Sends a log message through the server.
Starts an MCP server with the given options.
Types
Callbacks
@callback handle_initialize(params :: map(), state()) :: {:ok, map(), state()} | {:error, term(), state()}
Callback for handling initialization requests.
Called when a client sends an initialize request. Allows custom version negotiation and capability setup.
@callback handle_prompt_get(prompt_name :: String.t(), arguments :: map(), state()) :: {:ok, %{messages: [map()]}, state()} | {:error, term(), state()}
Callback for handling prompt requests.
Called when a client requests a prompt defined with defprompt.
Callback for listing prompts.
Called when a client requests a list of available prompts.
@callback handle_request(method :: String.t(), params :: map(), state()) :: {:reply, map(), state()} | {:error, term(), state()} | {:noreply, state()}
Callback for custom request handling.
Called for any requests not handled by the standard callbacks. Can be used for experimental features.
Callback for listing resources.
Called when a client requests a list of available resources.
@callback handle_resource_read(uri :: String.t(), full_uri :: String.t(), state()) :: {:ok, [content()], state()} | {:error, term(), state()}
Callback for handling resource reads.
Called when a client reads a resource defined with defresource.
@callback handle_resource_subscribe(uri :: String.t(), state()) :: {:ok, state()} | {:error, term(), state()}
Callback for resource subscriptions.
Called when a client subscribes to resource change notifications.
@callback handle_resource_unsubscribe(uri :: String.t(), state()) :: {:ok, state()} | {:error, term(), state()}
Callback for resource unsubscriptions.
Called when a client unsubscribes from resource change notifications.
@callback handle_tool_call(tool_name :: String.t(), arguments :: map(), state()) :: {:ok, tool_result(), state()} | {:error, term(), state()}
Callback for handling tool calls.
Called when a client invokes a tool defined with deftool.
Functions
@spec cancel_request(GenServer.server(), ExMCP.Types.request_id(), String.t() | nil) :: :ok
Sends a cancellation notification to the server.
This function allows external processes to notify the server that a request should be cancelled. The server will attempt to cancel the request if it's still pending.
Parameters
server- Server process referencerequest_id- The ID of the request to cancelreason- Optional human-readable reason for cancellation
Returns
:ok- Cancellation notification sent
Examples
:ok = ExMCP.Server.cancel_request(server, "req_123", "User cancelled")
:ok = ExMCP.Server.cancel_request(server, 12345, nil)
@spec create_message(GenServer.server(), map()) :: {:ok, map()} | {:error, term()}
Sends a createMessage request to the connected client.
This function allows the server to request message creation from the client using the sampling/createMessage method. This is part of the MCP sampling feature where servers can ask clients to generate LLM responses.
Parameters
server- Server process referenceparams- Parameters for message creation including messages, modelPreferences, etc.
Returns
{:ok, result}- The created message response from the client{:error, reason}- If the request fails
Examples
params = %{
"messages" => [
%{"role" => "user", "content" => %{"type" => "text", "text" => "Hello"}}
],
"modelPreferences" => %{
"hints" => [%{"name" => "gpt-4"}]
}
}
{:ok, response} = ExMCP.Server.create_message(server, params)
@spec get_pending_requests(GenServer.server()) :: [ExMCP.Types.request_id()]
Gets the list of pending request IDs on the server.
Returns a list of request IDs for requests that are currently being processed by the server. This can be used to monitor server load and track long-running operations.
Examples
{:ok, server} = MyServer.start_link()
# Get pending requests
pending = ExMCP.Server.get_pending_requests(server)
# => ["req_123", "req_456"]
@spec list_roots(GenServer.server(), timeout()) :: {:ok, %{roots: [map()]}} | {:error, any()}
Lists the roots available from the connected client.
Sends a roots/list request to the client to discover what filesystem or conceptual roots the client has access to. This allows the server to understand what the client can provide access to.
Parameters
server- Server process referencetimeout- Request timeout in milliseconds (default: 5000)
Returns
{:ok, %{roots: [root()]}}- List of roots from client{:error, reason}- Request failed
Root Format
Each root contains:
uri- URI identifying the root location (required)name- Human-readable name for the root (optional)
Examples
{:ok, %{roots: roots}} = ExMCP.Server.list_roots(server)
# Example roots format:
[
%{uri: "file:///home/user", name: "Home Directory"},
%{uri: "file:///projects", name: "Projects"},
%{uri: "config://app", name: "App Configuration"}
]
Sends a progress notification to the client.
Used for long-running operations to report progress updates.
@spec notify_prompts_changed(GenServer.server()) :: :ok
Notifies subscribed clients that the prompts list has changed.
Sends a resource update notification for subscribed clients.
This function should be called by the server when a subscribed resource changes.
@spec notify_resource_updated(GenServer.server(), String.t()) :: :ok
Alias for notify_resource_update/2 for backward compatibility.
@spec notify_resources_changed(GenServer.server()) :: :ok
Notifies subscribed clients that the resource list has changed.
@spec notify_roots_changed(GenServer.server()) :: :ok
Notifies the client that the server's available roots have changed.
Sends a notification to inform the client that the list of roots the server can access has been updated. This allows clients to refresh their understanding of what the server can provide.
Parameters
server- Server process reference
Returns
:ok- Notification sent successfully
Example
:ok = ExMCP.Server.notify_roots_changed(server)
@spec notify_tools_changed(GenServer.server()) :: :ok
Notifies subscribed clients that the tools list has changed.
@spec ping(GenServer.server(), timeout()) :: {:ok, map()} | {:error, any()}
Sends a ping request to the client.
The client must respond promptly or may be disconnected.
Sends a log message through the server.
Compatibility function for the logging system.
Starts an MCP server with the given options.
This function provides compatibility with the legacy server API and delegates to the appropriate server implementation.
Options
:handler- Handler module implementing ExMCP.Server.Handler:transport- Transport type (:stdio, :http, :test, etc.)- Other options are passed to the underlying implementation