Packages

Model Context Protocol (MCP) implementation in Elixir with Phoenix integration

Retired package: Release invalid - Accidental version. Use 0.3.0 instead.

Current section

Files

Jump to
backplane_mcp_protocol lib backplane mcp_protocol server task_store.ex
Raw

lib/backplane/mcp_protocol/server/task_store.ex

defmodule Backplane.McpProtocol.Server.TaskStore do
@moduledoc """
Behaviour for pluggable MCP task storage backends.
A TaskStore tracks `Backplane.McpProtocol.Server.Task` entries scoped to a session.
Adapters are wired via the `:task_store` option of `Backplane.McpProtocol.Server.Supervisor`
using the same `{module, opts}` shape as `:registry` and `:supervisor`:
{Backplane.McpProtocol.Server, transport: :stdio, task_store: {MyApp.HordeTaskStore, []}}
Phase 1 ships with the in-memory `Backplane.McpProtocol.Server.TaskStore.Local` adapter.
Distributed adapters (e.g. Horde-backed) plug in through this contract
without API changes.
## Naming
Adapters can either be named processes (default — server boots them under its
supervision tree using `Backplane.McpProtocol.Server.Registry.task_store_name/1`) or expose
a custom name (`:via` tuple, registered atom in another node, etc.) via the
optional `resolve_name/2` callback.
When `resolve_name/2` is implemented and returns a `:via` tuple, the adapter
is responsible for its own registration — the server supervisor will skip the
default child spec via `child_spec/1` returning `:ignore`.
"""
alias Backplane.McpProtocol.Server.Task
@type name :: term()
@type session_id :: String.t()
@type task_id :: String.t()
@callback child_spec(keyword()) :: Supervisor.child_spec() | :ignore
@callback put(name(), session_id(), Task.t()) :: :ok | {:error, term()}
@callback get(name(), session_id(), task_id()) :: {:ok, Task.t()} | {:error, :not_found}
@callback update(name(), session_id(), task_id(), (Task.t() -> Task.t())) ::
{:ok, Task.t()} | {:error, :not_found}
@callback delete(name(), session_id(), task_id()) :: :ok
@callback list_by_session(name(), session_id()) :: [Task.t()]
@doc """
Optional. Returns the name used to address the store for a given server.
Defaults to `Backplane.McpProtocol.Server.Registry.task_store_name(server)` when not
implemented. Override to return a `:via` tuple for distributed adapters.
"""
@callback resolve_name(server :: module(), opts :: keyword()) :: name()
@optional_callbacks resolve_name: 2
@doc """
Resolves the configured task store name for a server, asking the adapter if
it implements `resolve_name/2` and falling back to the default atom naming.
Uses `Code.ensure_loaded?/1` first because in releases the adapter beam may
exist on disk but not yet be loaded into the VM, in which case
`function_exported?/3` silently returns false and we'd skip the override.
"""
@spec resolve_name(module(), module(), keyword()) :: name()
def resolve_name(adapter, server, opts) do
if Code.ensure_loaded?(adapter) and function_exported?(adapter, :resolve_name, 2) do
adapter.resolve_name(server, opts)
else
Backplane.McpProtocol.Server.Registry.task_store_name(server)
end
end
end