AttestoMCP.Server.SessionStore behaviour (attesto_mcp_server v0.14.0)

Copy Markdown View Source

Durable session storage contract for the session-bound MCP revisions.

Keys are {namespace, session_id} data tuples. Adapters must never convert either component to an atom. update/3, update_ttl/3, and cleanup_expired/1 must be atomic with respect to one another. Records are the versioned JSON-compatible maps documented by AttestoMCP.Server.Session.to_record/1; unknown fields must be preserved by an adapter and ignored by the server. Records with a future format_version are opaque to an older node and must remain untouched by ordinary loads, updates, touches, and expiry cleanup. A preserved future record returned by update/3 or update_ttl/3 is reported as {:ok, opaque_record} so callers can distinguish it from an absent row. update_ttl/3 must delete an already expired record with a version understood by the adapter and return :not_found rather than renewing it. On success it must return the updated record. If the supplied timestamp is older than the record's current "last_seen_ms", adapters must retain the newer value; a successful touch must never rewind session activity. Adapters may implement count_active/1 to let server stats count sessions without materializing bounded record pages; otherwise the server falls back to list_active/1. For externally supplied keys, load/2, update/3, and update_ttl/3 should treat malformed or unrepresentable keys as absent, while delete/2 should remain idempotent; valid keys from another configured namespace must still return the namespace-mismatch error.

Summary

Types

A configured adapter module and its opaque store handle.

Types

adapter()

@type adapter() :: {module(), store()}

A configured adapter module and its opaque store handle.

key()

@type key() :: {String.t(), String.t()}

session_record()

@type session_record() :: map()

store()

@type store() :: term()

update_fun()

@type update_fun() :: (session_record() ->
                   {:ok, session_record()} | :delete | {:error, term()})

Callbacks

cleanup_expired(store)

@callback cleanup_expired(store()) :: {:ok, [key()]} | {:error, term()}

count_active(store)

(optional)
@callback count_active(store()) :: {:ok, non_neg_integer()} | {:error, term()}

delete(store, key)

@callback delete(store(), key()) :: :ok | {:error, term()}

list_active(store)

@callback list_active(store()) :: {:ok, [{key(), session_record()}]} | {:error, term()}

load(store, key)

@callback load(store(), key()) :: {:ok, session_record()} | :not_found | {:error, term()}

save(store, key, session_record)

@callback save(store(), key(), session_record()) :: :ok | {:error, term()}

update(store, key, update_fun)

@callback update(store(), key(), update_fun()) ::
  {:ok, session_record()} | :not_found | {:error, term()}

update_ttl(store, key, non_neg_integer)

@callback update_ttl(store(), key(), non_neg_integer()) ::
  {:ok, session_record()} | :not_found | {:error, term()}