Hex.pm CI HexDocs License

MCPKit is a reusable MCP server runtime for Phoenix applications.

It gives host applications a small set of host-facing contracts for building policy-aware MCP servers over Streamable HTTP, with support for tools, prompts, resources, completions, persistent sessions, and a host-started runtime for active sessions.

MCP Support Matrix

AreaStatusNotes
Streamable HTTP transport (POST)Supported
SSE session stream (GET)Supported for active sessions
Session termination (DELETE)Supported
Lifecycle (initialize, ping, notifications/initialized)Supported
Tools (tools/list, tools/call)Supported
Prompts (prompts/list, prompts/get)Supported
Resources (resources/list, resources/read, resources/templates/list)Supported
Utilities (completion/complete, notifications/cancelled)Supported
Request-time policy enforcementVia MCPKit.Policy
Resource subscriptionsNot implemented
Resource update notificationsNot implemented
Logging capabilityNot implemented
Progress notificationsNot implemented
List-changed notificationsNot implemented
Public host API for runtime-initiated client requestsInternal plumbing exists, but no public host API yet

Installation

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

Quickstart

1. Define the host contract

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

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

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

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

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

2. Implement persistent session storage

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
    # Load from your database or cache.
    {:error, :not_found}
  end

  @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

3. Start the runtime

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

4. Add a policy

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

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

5. Define 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

6. 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

mcp_scope/2 infers the definition module as MyApp.MCP.Definition and the runtime name as MyApp.MCP.Runtime. Override them with definition: and runtime: if needed.

Inside mcp_scope, module references follow normal Phoenix scope aliasing, so Tools.Ping resolves as MyApp.MCP.Tools.Ping and remains visible to editor tooling.

Host Contracts

The host application is responsible for:

See the guides for fuller examples:

Router DSL

Use MCPKit.Router.mcp_scope/2 or mcp_scope/3 inside a Phoenix router scope.

mcp_scope "/mcp", MyApp.MCP do
  tool "project_create", Tools.ProjectCreate
  prompt "draft_release_notes", Prompts.DraftReleaseNotes
  resource "project", Resources.Project
end

Inside mcp_scope, you can declare:

  • tool/2
  • prompt/2
  • resource/2

Policy

MCPKit.Policy is evaluated on every request. It supports these decisions:

  • :allow
  • {:deny, :not_found}
  • {:deny, :forbidden}

Lists are filtered item-by-item, while direct calls, prompt gets, resource reads, and completions are enforced separately.

completion/complete is also policy-aware and only returns suggestions for visible prompt arguments and resource templates.

Authoring APIs

Tools

Tool modules use MCPKit.Tool to declare input schemas and return MCPKit.Response values.

Prompts

Prompt modules use MCPKit.Prompt to declare argument schemas, render MCP messages, and optionally implement complete/3 for argument suggestions.

Resources

Resource modules use MCPKit.Resource to declare concrete resource entries, URI templates, URI reads, and optionally implement complete/3 for template variable suggestions.

Supported MCP Surface

Currently implemented methods:

  • initialize
  • ping
  • completion/complete
  • prompts/list
  • prompts/get
  • resources/list
  • resources/read
  • resources/templates/list
  • tools/list
  • tools/call
  • notifications/initialized
  • notifications/cancelled
  • DELETE session termination
  • GET SSE session stream

For one canonical compatibility summary, see Supported MCP Surface.

Publishing Checklist

  • GitHub Actions publishes tagged releases automatically from v* tags
  • configure the HEX_API_KEY repository secret before pushing a release tag
  • update version in mix.exs
  • confirm package links and source URL
  • run mix test
  • run mix docs
  • run mix hex.build