Hermes.Server.Behaviour behaviour (hermes_mcp v0.10.0)
Defines the core behavior that all MCP servers must implement.
This module specifies the required callbacks that concrete server implementations must provide to handle MCP protocol interactions.
Summary
Callbacks
Handles incoming notifications from clients.
Handles incoming requests from clients.
Initializes the server state.
Returns server capabilities for initialization response. Optional callback with default implementation.
Returns server information for initialization response.
Returns the list of MCP protocol versions supported by this server.
Functions
Checks if the given module implements the Hermes.Server.Behaviour interface.
Types
Callbacks
@callback handle_notification( notification :: notification(), state :: Hermes.Server.Frame.t() ) :: {:noreply, new_state :: Hermes.Server.Frame.t()} | {:error, error :: mcp_error(), new_state :: Hermes.Server.Frame.t()}
Handles incoming notifications from clients.
This callback processes client notifications, which don't require responses.
Parameters
notification
- Map containing:"method"
- Notification method name"params"
- Optional parameters map
frame
- Current server frame
Return Values
{:noreply, frame}
- Notification processed successfully{:error, error, frame}
- Notification processing failed (error logged but not sent)
Examples
@impl Hermes.Server.Behaviour
def handle_notification(%{"method" => "textDocument/didChange", "params" => params}, frame) do
# Handle document change notification
document_id = params["uri"]
content = params["text"]
frame = Frame.update(frame, :documents, fn docs ->
Map.put(docs || %{}, document_id, content)
end)
{:noreply, frame}
end
@impl Hermes.Server.Behaviour
def handle_notification(%{"method" => "progress/update", "params" => %{"token" => token}}, frame) do
# Track progress updates
Logger.info("Progress update for token: #{token}")
{:noreply, frame}
end
# Ignore unknown notifications
@impl Hermes.Server.Behaviour
def handle_notification(_notification, frame) do
{:noreply, frame}
end
@callback handle_request(request :: request(), state :: Hermes.Server.Frame.t()) :: {:reply, response :: response(), new_state :: Hermes.Server.Frame.t()} | {:noreply, new_state :: Hermes.Server.Frame.t()} | {:error, error :: mcp_error(), new_state :: Hermes.Server.Frame.t()}
Handles incoming requests from clients.
This callback processes client requests and returns an appropriate response, along with updated server state.
Parameters
request
- Map containing:"id"
- Request ID"method"
- Method name (e.g., "tools/list", "prompts/get")"params"
- Optional parameters map
frame
- Current server frame with state and metadata
Return Values
{:reply, response, frame}
- Send response to client{:noreply, frame}
- No response needed{:error, error, frame}
- Request failed with error
Examples
@impl Hermes.Server.Behaviour
def handle_request(%{"method" => "custom/echo", "params" => params}, frame) do
{:reply, params, frame}
end
@impl Hermes.Server.Behaviour
def handle_request(%{"method" => "data/fetch", "params" => %{"id" => id}}, frame) do
case fetch_data(id) do
{:ok, data} ->
{:reply, %{"data" => data}, frame}
{:error, :not_found} ->
error = Hermes.MCP.Error.protocol(:invalid_params, %{message: "Data not found"})
{:error, error, frame}
end
end
@impl Hermes.Server.Behaviour
def handle_request(%{"method" => "async/start"}, frame) do
# Start async operation without immediate response
Task.start(fn -> do_async_work() end)
{:noreply, frame}
end
# Fallback for unknown methods
@impl Hermes.Server.Behaviour
def handle_request(_request, frame) do
{:error, Hermes.MCP.Error.protocol(:method_not_found), frame}
end
@callback init(init_arg :: term(), Hermes.Server.Frame.t()) :: {:ok, Hermes.Server.Frame.t()} | :ignore | {:stop, reason :: term()}
Initializes the server state.
This callback is invoked when the server is started and should perform any necessary setup, returning the initial state.
Parameters
init_arg
- Arguments passed fromstart_link/3
frame
- Initial server frame
Return Values
{:ok, frame}
- Server initialized successfully with updated frame:ignore
- Server initialization ignored, process not started{:stop, reason}
- Server initialization failed
Examples
@impl Hermes.Server.Behaviour
def init(_args, frame) do
# Simple initialization
{:ok, frame}
end
@impl Hermes.Server.Behaviour
def init(%{config_file: path}, frame) do
# Initialize with configuration
case load_config(path) do
{:ok, config} ->
frame = Frame.assign(frame, :config, config)
{:ok, frame}
{:error, reason} ->
{:stop, {:config_error, reason}}
end
end
@impl Hermes.Server.Behaviour
def init(%{port: port}, frame) when port < 1024 do
# Validation example
{:stop, {:invalid_port, port}}
end
@callback server_capabilities() :: server_capabilities()
Returns server capabilities for initialization response. Optional callback with default implementation.
Return Value
Map containing capability configurations:
"tools"
- Tool execution support"prompts"
- Prompt template support"resources"
- Resource provision support"logging"
- Log level configuration support
Examples
@impl Hermes.Server.Behaviour
def server_capabilities do
%{
"tools" => %{},
"prompts" => %{
"listChanged" => true # Notify on prompt list changes
},
"resources" => %{
"subscribe" => true, # Support resource subscriptions
"listChanged" => true # Notify on resource list changes
},
"logging" => %{}
}
end
@callback server_info() :: server_info()
Returns server information for initialization response.
Return Value
Map containing:
"name"
- Server name (required)"version"
- Server version (required)
Examples
@impl Hermes.Server.Behaviour
def server_info do
%{
"name" => "My MCP Server",
"version" => "1.0.0"
}
end
@callback supported_protocol_versions() :: [String.t()]
Returns the list of MCP protocol versions supported by this server.
Examples
iex> MyServer.supported_protocol_versions()
["2024-11-05", "2025-03-26"]