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
Current section
Files
lib/backplane/mcp_protocol/protocol/registry.ex
defmodule Backplane.McpProtocol.Protocol.Registry do
@moduledoc """
Registry for MCP protocol version modules.
Maps version strings to their implementing modules, supports version negotiation,
and provides the central dispatch point for version-specific protocol logic.
## Usage
iex> Backplane.McpProtocol.Protocol.Registry.get("2025-11-25")
{:ok, Backplane.McpProtocol.Protocol.V2025_11_25}
iex> Backplane.McpProtocol.Protocol.Registry.supported_versions()
["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"]
iex> Backplane.McpProtocol.Protocol.Registry.negotiate("2025-03-26")
{:ok, "2025-03-26", Backplane.McpProtocol.Protocol.V2025_03_26}
"""
@versions %{
"2024-11-05" => Backplane.McpProtocol.Protocol.V2024_11_05,
"2025-03-26" => Backplane.McpProtocol.Protocol.V2025_03_26,
"2025-06-18" => Backplane.McpProtocol.Protocol.V2025_06_18,
"2025-11-25" => Backplane.McpProtocol.Protocol.V2025_11_25
}
@latest_version "2025-11-25"
@fallback_version "2025-03-26"
@type version :: String.t()
@doc """
Get the protocol module for a given version string.
## Examples
iex> Backplane.McpProtocol.Protocol.Registry.get("2025-06-18")
{:ok, Backplane.McpProtocol.Protocol.V2025_06_18}
iex> Backplane.McpProtocol.Protocol.Registry.get("unknown")
:error
"""
@spec get(version()) :: {:ok, module()} | :error
def get(version), do: Map.fetch(@versions, version)
@doc """
List all supported versions in preference order (newest first).
"""
@spec supported_versions() :: [version()]
def supported_versions do
@versions |> Map.keys() |> Enum.sort(:desc)
end
@doc """
Returns the latest supported protocol version string.
"""
@spec latest_version() :: version()
def latest_version, do: @latest_version
@doc """
Returns the fallback protocol version for compatibility.
"""
@spec fallback_version() :: version()
def fallback_version, do: @fallback_version
@doc """
Returns the module for the latest supported protocol version.
"""
@spec latest_module() :: module()
def latest_module, do: @versions[@latest_version]
@doc """
Check if a version string is supported.
"""
@spec supported?(version()) :: boolean()
def supported?(version), do: Map.has_key?(@versions, version)
@doc """
Negotiate the best version given a client's requested version.
MCP spec: the server picks the version, the client proposes one.
If we support the requested version, use it. Otherwise, return an error
with the list of supported versions.
## Examples
iex> Backplane.McpProtocol.Protocol.Registry.negotiate("2025-11-25")
{:ok, "2025-11-25", Backplane.McpProtocol.Protocol.V2025_11_25}
iex> Backplane.McpProtocol.Protocol.Registry.negotiate("9999-01-01")
{:error, :unsupported_version, ["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"]}
"""
@spec negotiate(version()) :: {:ok, version(), module()} | {:error, :unsupported_version, [version()]}
def negotiate(client_version) do
case get(client_version) do
{:ok, mod} -> {:ok, client_version, mod}
:error -> {:error, :unsupported_version, supported_versions()}
end
end
@doc """
Negotiate version between client and server supported version lists.
Used when the server has a restricted set of supported versions.
Returns the best matching version (client's preference if in server list,
otherwise server's latest).
## Examples
iex> Backplane.McpProtocol.Protocol.Registry.negotiate("2025-03-26", ["2025-11-25", "2025-03-26"])
{:ok, "2025-03-26", Backplane.McpProtocol.Protocol.V2025_03_26}
iex> Backplane.McpProtocol.Protocol.Registry.negotiate("2024-11-05", ["2025-11-25", "2025-03-26"])
{:ok, "2025-11-25", Backplane.McpProtocol.Protocol.V2025_11_25}
"""
@spec negotiate(version(), [version()]) :: {:ok, version(), module()} | :error
def negotiate(client_version, [latest | _] = server_versions) do
version =
if client_version in server_versions do
client_version
else
latest
end
case get(version) do
{:ok, mod} -> {:ok, version, mod}
:error -> :error
end
end
@doc """
Returns the features supported by a given version.
Delegates to the version module's `supported_features/0` callback.
"""
@spec get_features(version()) :: {:ok, [atom()]} | :error
def get_features(version) do
case get(version) do
{:ok, mod} -> {:ok, mod.supported_features()}
:error -> :error
end
end
@doc """
Checks if a feature is supported by a protocol version.
"""
@spec supports_feature?(version(), atom()) :: boolean()
def supports_feature?(version, feature) when is_binary(version) and is_atom(feature) do
case get_features(version) do
{:ok, features} -> feature in features
:error -> false
end
end
@doc """
Returns the progress notification params schema for a given version.
Delegates to the version module's `progress_params_schema/0` callback.
"""
@spec progress_params_schema(version()) :: {:ok, map()} | :error
def progress_params_schema(version) do
case get(version) do
{:ok, mod} -> {:ok, mod.progress_params_schema()}
:error -> :error
end
end
end