McpServer behaviour (HTTP MCP Server v0.6.0)

View Source

Defines the behaviour for implementing Model Context Protocol (MCP) servers in Elixir.

McpServer is the core behaviour module that specifies the callback functions required to implement a fully-functional MCP server. MCP (Model Context Protocol) is a protocol that enables AI models to interact with external tools, prompts, and resources.

Overview

This behaviour defines optional callbacks for three main MCP capabilities:

  1. Tools - Functions that can be called by MCP clients with input validation
  2. Prompts - Interactive message templates with argument completion
  3. Resources - Data sources that can be read and completed

Implementing modules can choose to implement any combination of these capabilities by defining the appropriate callback functions.

Callbacks

All callbacks are optional, allowing servers to implement only the features they need:

Tool Callbacks

  • list_tools/1 - Returns the list of available tools with their schemas
  • call_tool/3 - Executes a tool with the given name and arguments

Prompt Callbacks

  • get_prompt/3 - Returns a prompt's messages for the given arguments
  • complete_prompt/3 - Provides completion suggestions for prompt arguments

Resource Callbacks

  • list_resources/1 - Returns the list of available resources
  • read_resource/3 - Reads and returns a resource's contents
  • complete_resource/3 - Provides completion suggestions for resource URIs

Connection Context

All callbacks receive a McpServer.Conn.t() as their first argument, which provides request context and connection information for handling the MCP interaction.

Usage

Instead of implementing this behaviour directly, most applications should use McpServer.Router, which provides a convenient DSL for defining tools, prompts, and resources:

defmodule MyApp.Router do
  use McpServer.Router

  tool "greet", "Greets a person", MyController, :greet do
    input_field("name", "The name to greet", :string, required: true)
    output_field("greeting", "The greeting message", :string)
  end

  prompt "welcome", "A friendly welcome prompt" do
    argument("user_name", "The user's name", required: true)
    get MyController, :get_welcome_prompt
  end

  resource "user", "https://example.com/users/{id}" do
    description "User resource"
    read MyController, :read_user
  end
end

Error Handling

Callbacks that can fail should return an {:error, code, message} tuple where:

  • code is an integer error code (typically JSON-RPC error codes)
  • message is a human-readable error description

Example Implementation

defmodule MyMcpServer do
  @behaviour McpServer

  @impl true
  def list_tools(_conn) do
    {:ok, [
      %{
        "name" => "echo",
        "description" => "Echoes back the input",
        "inputSchema" => %{
          "type" => "object",
          "properties" => %{"message" => %{"type" => "string"}},
          "required" => ["message"]
        }
      }
    ]}
  end

  @impl true
  def call_tool(_conn, "echo", %{"message" => msg}) do
    {:ok, %{"response" => msg}}
  end
end

See McpServer.Router for a more convenient way to define MCP servers.

Summary

Types

error()

@type error() :: {:error, message :: String.t()}

Callbacks

call_tool(t, t, map)

(optional)
@callback call_tool(McpServer.Conn.t(), String.t(), map()) :: {:ok, map()} | error()

complete_prompt(t, prompt_name, argument_name, prefix)

(optional)
@callback complete_prompt(
  McpServer.Conn.t(),
  prompt_name :: String.t(),
  argument_name :: String.t(),
  prefix :: String.t()
) :: list() | error()

complete_resource(t, resource_name, argument_name, prefix)

(optional)
@callback complete_resource(
  McpServer.Conn.t(),
  resource_name :: String.t(),
  argument_name :: String.t(),
  prefix :: String.t()
) :: list() | error()

get_prompt(t, t, map)

(optional)
@callback get_prompt(McpServer.Conn.t(), String.t(), map()) :: {:ok, list()} | error()

list_resources(t)

(optional)
@callback list_resources(McpServer.Conn.t()) :: {:ok, list()} | error()

list_tools(t)

(optional)
@callback list_tools(McpServer.Conn.t()) :: {:ok, list()} | error()

read_resource(t, t, map)

(optional)
@callback read_resource(McpServer.Conn.t(), String.t(), map()) :: {:ok, any()} | error()