This guide shows the smallest complete host integration for mcp_kit.

Install

def deps do
  [
    {:mcp_kit, "~> 0.2.4"}
  ]
end

Define the MCP host contract

defmodule MyApp.MCP.Definition do
  @behaviour MCPKit.Definition

  @impl true
  def server_info, do: %{"name" => "my-app", "version" => "0.1.0"}

  @impl true
  def session_store, do: MyApp.MCP.SessionStore

  @impl true
  def policy, do: MyApp.MCP.Policy
end

mcp_kit owns the MCP transport version and advertises its built-in current protocol version automatically. Host apps do not configure this.

Implement durable sessions

defmodule MyApp.MCP.SessionStore do
  @behaviour MCPKit.SessionStore

  @impl true
  def create_session(attrs) do
    {:ok,
     %{
       id: Ecto.UUID.generate(),
       initialized: false,
       protocol_version: attrs.protocol_version,
       client_info: attrs.client_info,
       client_capabilities: attrs.client_capabilities
     }}
  end

  @impl true
  def fetch_session(_session_id), do: {:error, :not_found}

  @impl true
  def touch_session(session), do: {:ok, session}

  @impl true
  def mark_initialized(session), do: {:ok, %{session | initialized: true}}

  @impl true
  def delete_session(_session_id), do: {:error, :not_found}
end

Start the runtime

children = [
  {MCPKit.Runtime, definition: MyApp.MCP.Definition}
]

Add a basic policy

defmodule MyApp.MCP.Policy do
  @behaviour MCPKit.Policy

  @impl true
  def authorize(_action, _context), do: :allow
end

Author a tool

defmodule MyApp.MCP.Tools.Ping do
  use MCPKit.Tool

  alias MCPKit.Response

  schema do
    field :message, :string, required: true
  end

  @impl true
  def execute(arguments, context) do
    {:reply, Response.tool() |> Response.structured(arguments), context}
  end
end

Mount the MCP route

defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  import MCPKit.Router

  mcp_scope "/mcp", MyApp.MCP do
    tool "ping", Tools.Ping
  end
end

Like a normal Phoenix scope, Tools.Ping resolves to MyApp.MCP.Tools.Ping inside mcp_scope.

Next steps