defmodule Backplane.McpProtocol.MCP.ID do @moduledoc false @doc """ Generates a unique request ID. Creates a Base64 encoded string containing: - Timestamp component (nanoseconds) - Process identifier hash - Random component ## Examples iex> id = Backplane.McpProtocol.MCP.ID.generate() iex> is_binary(id) true """ @spec generate() :: String.t() def generate do binary = << System.system_time(:nanosecond)::64, :erlang.phash2({node(), self()}, 16_777_216)::24, :rand.uniform(16_777_216)::24 >> Base.url_encode64(binary) end @doc """ Generates a unique request ID. Creates a standard ID with a "req_" prefix for clarity. Request IDs are used to correlate requests with their responses. ## Examples iex> id = Backplane.McpProtocol.MCP.ID.generate_request_id() iex> String.starts_with?(id, "req_") true """ @spec generate_request_id() :: String.t() def generate_request_id do "req_" <> generate() end @doc """ Generates a unique error ID. Creates a standard ID with a "err_" prefix for clarity. error IDs are used to correlate errors with their responses. ## Examples iex> id = Backplane.McpProtocol.MCP.ID.generate_error_id() iex> String.starts_with?(id, "err_") true """ @spec generate_error_id() :: String.t() def generate_error_id do "err_" <> generate() end @doc """ Generates a unique progress token. Creates a standard request ID with a "progress_" prefix for clarity. Progress tokens are used in the MCP protocol to track long-running operations. ## Examples iex> token = Backplane.McpProtocol.MCP.ID.generate_progress_token() iex> String.starts_with?(token, "progress_") true """ @spec generate_progress_token() :: String.t() def generate_progress_token do "progress_" <> generate() end @doc """ Generates a unique session ID. Creates a standard ID with a "session_" prefix for clarity. Session IDs are used to track HTTP sessions in transports. ## Examples iex> id = Backplane.McpProtocol.MCP.ID.generate_session_id() iex> String.starts_with?(id, "session_") true """ @spec generate_session_id() :: String.t() def generate_session_id do "session_" <> generate() end @doc """ Extracts timestamp from an ID for debugging purposes. This is primarily useful for troubleshooting timing issues or analyzing the sequence of requests. ## Parameters * `id` - An ID generated by this module ## Returns * The timestamp (in nanoseconds) or nil if the ID cannot be parsed ## Examples iex> id = Backplane.McpProtocol.MCP.ID.generate() iex> timestamp = Backplane.McpProtocol.MCP.ID.timestamp_from_id(id) iex> is_integer(timestamp) true iex> Backplane.McpProtocol.MCP.ID.timestamp_from_id("invalid-id") nil """ @spec timestamp_from_id(String.t()) :: integer() | nil def timestamp_from_id(id) when is_binary(id) do case Base.url_decode64(id) do {:ok, <>} -> timestamp _ -> nil end end @doc """ Checks if a string appears to be a valid MCP ID. This performs a basic validation to check if the string conforms to the expected ID format. ## Examples iex> id = Backplane.McpProtocol.MCP.ID.generate() iex> Backplane.McpProtocol.MCP.ID.valid?(id) true iex> Backplane.McpProtocol.MCP.ID.valid?("invalid-id") false """ @spec valid?(term()) :: boolean() def valid?(id) when is_binary(id) do case Base.url_decode64(id) do {:ok, <<_timestamp::64, _process::24, _random::24>>} -> true _ -> false end end def valid?(_), do: false @doc """ Checks if a string appears to be a valid request ID. Validates that the string starts with "req_" and the remainder is a valid MCP ID. ## Examples iex> id = Backplane.McpProtocol.MCP.ID.generate_request_id() iex> Backplane.McpProtocol.MCP.ID.valid_request_id?(id) true iex> Backplane.McpProtocol.MCP.ID.valid_request_id?("not-an-id") false """ @spec valid_request_id?(term()) :: boolean() def valid_request_id?(id) when is_binary(id) do case String.split_at(id, 4) do {"req_", rest} -> valid?(rest) _ -> false end end def valid_request_id?(_), do: false @doc """ Checks if a string appears to be a valid progress token. Validates that the string starts with "progress_" and the remainder is a valid MCP ID. ## Examples iex> token = Backplane.McpProtocol.MCP.ID.generate_progress_token() iex> Backplane.McpProtocol.MCP.ID.valid_progress_token?(token) true iex> Backplane.McpProtocol.MCP.ID.valid_progress_token?("not-a-token") false """ @spec valid_progress_token?(term()) :: boolean() def valid_progress_token?(token) when is_binary(token) do case String.split_at(token, 9) do {"progress_", rest} -> valid?(rest) _ -> false end end def valid_progress_token?(_), do: false end