The main behaviour for MCP servers.
Implement this behaviour to create a custom MCP server. If you are looking for a high-level framework to build tools and resources, see GenMCP.Suite.
Example
defmodule MyServer do
@behaviour GenMCP
alias GenMCP.MCP
@impl true
def init(_session_id, _opts) do
{:ok, %{}}
end
@impl true
def handle_request(%MCP.InitializeRequest{} = req, _channel, state) do
# Protocol version check omitted for brevity
result = MCP.intialize_result(
capabilities: MCP.capabilities(tools: true),
server_info: MCP.server_info(name: "My Server", version: "1.0.0")
)
{:reply, {:result, result}, state}
end
def handle_request(%MCP.ListToolsRequest{}, _channel, state) do
result = MCP.list_tools_result([
%MCP.Tool{
name: "hello",
description: "Say hello",
inputSchema: %{
type: "object",
properties: %{
name: %{type: "string"}
}
}
}
])
{:reply, {:result, result}, state}
end
@impl true
def handle_notification(%MCP.InitializedNotification{}, state) do
{:noreply, state}
end
def handle_notification(_notif, state) do
{:noreply, state}
end
@impl true
def handle_info(_msg, state) do
{:noreply, state}
end
end
Summary
Callbacks
Handles process messages.
Handles an incoming MCP notification.
Handles an incoming MCP request and returns a result or stop the server.
Initializes the server state.
Called when a session is deleted by the client.
This callback is called during session initialization when a non-initialization request (such as a call tool request) is received and there is no current OTP process tied to the session id.
Called when a session is restored by the GenMCP.Suite.SessionController
implementation.
Called when a session times out.
Functions
The gen_mcp application uses telemetry events to publish various application lifecycle events. This can be used to log only what is important to you.
Returns the default logging level used by the MCP logging features on session initialization.
Returns the supported protocol versions.
Types
@type notification() :: GenMCP.MCP.InitializedNotification.t() | GenMCP.MCP.CancelledNotification.t() | GenMCP.MCP.RootsListChangedNotification.t() | GenMCP.MCP.ProgressNotification.t()
@type request() :: GenMCP.MCP.InitializeRequest.t() | GenMCP.MCP.ListToolsRequest.t() | GenMCP.MCP.CallToolRequest.t() | GenMCP.MCP.ListResourcesRequest.t() | GenMCP.MCP.ReadResourceRequest.t() | GenMCP.MCP.ListResourceTemplatesRequest.t() | GenMCP.MCP.ListPromptsRequest.t() | GenMCP.MCP.GetPromptRequest.t() | GenMCP.MCP.PingRequest.t()
@type result() :: GenMCP.MCP.InitializeResult.t() | GenMCP.MCP.ListToolsResult.t() | GenMCP.MCP.CallToolResult.t() | GenMCP.MCP.ListResourcesResult.t() | GenMCP.MCP.ReadResourceResult.t() | GenMCP.MCP.ListResourceTemplatesResult.t() | GenMCP.MCP.ListPromptsResult.t() | GenMCP.MCP.GetPromptResult.t()
@type session_id() :: String.t()
@type state() :: term()
Callbacks
Handles process messages.
Invoked when the server process receives a message that is not an MCP request or notification.
@callback handle_notification(notification(), state()) :: {:noreply, state()}
Handles an incoming MCP notification.
Notifications are one-way messages that do not expect a response.
@callback handle_request(request(), GenMCP.Mux.Channel.t(), state()) :: {:reply, server_reply(), state()} | {:stop, reason :: term(), server_reply_nostream(), state()}
Handles an incoming MCP request and returns a result or stop the server.
@callback init(session_id(), init_arg :: term()) :: {:ok, state()} | {:stop, term()}
Initializes the server state.
Called when a new MCP session is established.
Called when a session is deleted by the client.
Return value is not checked, and the server is shut down immediately.
@callback session_fetch( session_id(), channel :: GenMCP.Mux.Channel.t(), init_arg :: term() ) :: {:ok, GenMCP.Suite.SessionController.restore_data()} | {:error, :not_found}
This callback is called during session initialization when a non-initialization request (such as a call tool request) is received and there is no current OTP process tied to the session id.
This is a raw callback
The call is made from the HTTP transport process, giving raw initialization
args for the server. It is called before the init/2 callback is called
and there is no possibility to return a new state or arg.
The returned data will be passed to the session_restore/3 callback after
server process initialization.
@callback session_restore( GenMCP.Suite.SessionController.restore_data(), channel :: GenMCP.Mux.Channel.t(), state() ) :: {:noreply, state()} | {:stop, reason :: term(), state()}
Called when a session is restored by the GenMCP.Suite.SessionController
implementation.
Your server init/2 callback will have been called before, but there will
be no call of handle_request/3 with an initialization request.
The next call will be either another request or a notification.
Called when a session times out.
Functions
The gen_mcp application uses telemetry events to publish various application lifecycle events. This can be used to log only what is important to you.
The telemetry logger will log all telemetry events by default, at various log levels (debug, info, warning, error , etc.).
Two filters are supported:
:min_log_level- For instance if:erroris given, the default logger will not log events for which it uses the lower levels. This allows you to still have logs for errors, without cluttering info and debug logs.:prefixes- A list of event prefixes (which are a list too) to match. The logger will only log events whose prefixes match one of the the given prefixes. For instance,[[:gen_mcp, :cluster], [:gen_mcp, :session]]will only log events related to the cluster and sessions.
Both filters are compatible.
See GenMCP.TelemetryLogger for a list of all emitted events.
Returns the default logging level used by the MCP logging features on session initialization.
Returns the supported protocol versions.