Stable host-facing facade for AttestoMCP.Server.
Registration is performed before serving traffic. Definitions use JSON-style
string keys for schemas and wire values. Handler callbacks receive a
primitive-specific input plus an authorization context and return
{:ok, result}, {:error, reason}, or the modern
{:input_required, requests} MRTR form.
The facade intentionally exposes no task profile in this release. Calls that would enable modern or legacy Tasks return the dated method-not-found result.
Two-arity handler callbacks use handler.(input, context); one-arity handlers
receive only input, and MFA handlers follow the same input/context order. A
tool's input is its arguments map. A prompt receives
%{name: name, arguments: arguments}. A resource receives
%{uri: uri, params: template_params}, and a completion receives
%{ref: ref, argument: argument, value: value, context: context}. These
envelopes use atom keys for their declared fields; a resource MRTR retry also
carries its string-keyed input-response entries at the top level. Nested MCP
values retain their JSON string keys by default. The server-wide
tool_argument_keys: :atoms opt-in converts only literal, schema-declared
property keys that already exist as atoms; it never creates atoms from
schemas or client input. The callback context contains the
complete authenticated principal, tenant, scopes, transport, negotiated
version, request ID, trace_context, progress callback, and an optional
stable :principal_binding supplied by the transport. The HTTP Plug always
supplies that field, defaulting it to the complete principal when no binding
callback is configured. It also supplies the Attesto assigns (:attesto_mcp_claims,
:attesto_mcp_scopes, :attesto_mcp_sender, :attesto_mcp_principal, and
:attesto_context) when the Plug boundary is used. The context also exposes
the supervised server's :max_json_bytes, :output_canonicalization, and
:tool_argument_keys values, and :request_meta, an immutable snapshot of
the request's untrusted params["_meta"] object (see
AttestoMCP.Server.RequestMeta). AttestoMCP.Server.Result.tool_from_context/2,3
consumes the first two automatically; low-level constructors can still use
them as explicit options. A successful callback
returns {:ok, result}, an application failure returns {:error, reason},
and an interactive callback returns {:input_required, request_map} with
typed MRTR request entries. An HTTP context_builder contributes only the
nested :host_context map. Use AttestoMCP.Server.Result.error/2 when an
error message and stable code are intentionally safe to disclose.
:instructions_provider and :tool_presentation customize server guidance
and visible tool descriptors for the current caller
(AttestoMCP.Server.Presentation); :cache_policy selects cache hints by
operation and definition (AttestoMCP.Server.CachePolicy); :server_icons
adds icons to the server's implementation identity; and
export_schema_dialect: true makes the default JSON Schema 2020-12 dialect
explicit in exported tool schemas without changing validation.
For interactive workflows with mode: "url", AttestoMCP.Server.API provides
staged, single-use, subject-bound approval records via stage_url_elicitation/5,
resolve_url_elicitation/3, and consume_url_elicitation/3.
Summary
Types
A cache scope; string values are restricted to "private" or "public".
A text, image, audio, resource-link, or embedded-resource item.
A registered primitive definition with JSON-compatible fields. required_scopes is the primary all-of clause and alternative_scope_sets supplies bounded alternative all-of clauses.
Authorization and transport context passed to handlers.
A normal, failed, or interactive handler return.
A host callback: a function, {module, function}, or {module, function, prefix_args}.
An MCP icon. src is an absolute HTTP/HTTPS URL or a Base64 data: URI with an
image/* media type; mimeType (or mime_type), sizes ("WxH" or
"any"), and theme ("light" or "dark") are optional.
A modern result carrying resultType and protocol metadata.
Interactive request-state and input-response payloads.
A prompt message with a user or assistant role and content item.
A per-publication authorization callback applied to every matching subscriber.
One primitive registration accepted by the atomic batch and startup APIs.
A decoded JSON-RPC request, notification, or response.
A text or Base64 resource content entry.
One conjunctive scope clause used by a primitive definition.
A supervised server pid or a registered server name.
A supported server startup option and its value.
Keyword options accepted by start_link/1; task flags are disabled in this release.
Modern subscription category/resource filters.
A normalized tool result with content and optional structured output.
Functions
Returns one bounded page of active legacy session IDs for operator tooling.
Cancels a request owned by the supplied principal binding.
Cancels a modern subscription.
Cancels a modern subscription owned by the given sink process.
Returns a supervision child spec keyed by the optional registered server name.
Closes a modern subscription.
Closes a modern subscription owned by the given sink process.
Atomically consumes a staged URL elicitation record upon confirmation.
Deletes a legacy session and its owned streams.
Dispatches one decoded request through the shared protocol core.
Looks up a session only for its principal binding and tenant.
Creates a principal-binding/tenant-bound legacy session.
Returns normalized startup options used by Plug and stdio adapters.
Queues a filtered modern notification and publishes its legacy event.
Registers one primitive of a supported type.
Atomically registers a bounded primitive batch with coalesced invalidations.
Registers a completion handler tied to an explicit prompt/template reference.
Registers a prompt definition, including required and optional arguments.
Registers a static resource definition.
Registers a URI-template resource definition.
Registers a tool definition and publishes a modern catalog invalidation.
Atomically replaces the complete primitive catalog from one bounded batch.
Resolves a staged URL elicitation record for the host's approval page.
Returns the registered primitive snapshot.
Stages a URL elicitation record before returning a mode: "url" input request.
Starts the supervised, transport-neutral MCP server.
Returns bounded public counters for active work and transport state.
Types
@type cache_scope() :: :private | :public | String.t()
A cache scope; string values are restricted to "private" or "public".
@type content_item() :: map()
A text, image, audio, resource-link, or embedded-resource item.
A registered primitive definition with JSON-compatible fields. required_scopes is the primary all-of clause and alternative_scope_sets supplies bounded alternative all-of clauses.
@type handler_context() :: map()
Authorization and transport context passed to handlers.
@type handler_return() :: {:ok, term()} | {:error, term()} | {:input_required, %{optional(String.t()) => map()}}
A normal, failed, or interactive handler return.
A host callback: a function, {module, function}, or {module, function, prefix_args}.
An MCP icon. src is an absolute HTTP/HTTPS URL or a Base64 data: URI with an
image/* media type; mimeType (or mime_type), sizes ("WxH" or
"any"), and theme ("light" or "dark") are optional.
@type modern_result() :: map()
A modern result carrying resultType and protocol metadata.
Interactive request-state and input-response payloads.
@type prompt_message() :: map()
A prompt message with a user or assistant role and content item.
A per-publication authorization callback applied to every matching subscriber.
One primitive registration accepted by the atomic batch and startup APIs.
@type request() :: map()
A decoded JSON-RPC request, notification, or response.
@type resource_content() :: map()
A text or Base64 resource content entry.
@type scope_set() :: [String.t()]
One conjunctive scope clause used by a primitive definition.
A supervised server pid or a registered server name.
@type server_option() :: {:name, atom()} | {:protocol_versions, [String.t()]} | {:max_concurrency, pos_integer()} | {:per_principal_concurrency, pos_integer()} | {:request_timeout, non_neg_integer()} | {:max_request_timeout, pos_integer()} | {:client_request_timeout, pos_integer()} | {:legacy_initialized_grace_ms, non_neg_integer()} | {:session_idle_timeout, pos_integer()} | {:session_absolute_timeout, pos_integer()} | {:max_sessions_per_principal, pos_integer()} | {:max_legacy_streams_per_session, pos_integer()} | {:max_json_bytes, pos_integer()} | {:output_canonicalization, :strict | :json | :jason} | {:tool_argument_keys, :strings | :atoms} | {:max_body_bytes, pos_integer()} | {:max_message_bytes, pos_integer()} | {:max_queue, pos_integer()} | {:stream_keepalive_ms, non_neg_integer()} | {:legacy_keepalive_ms, non_neg_integer()} | {:stream_queue_size, pos_integer()} | {:subscription_queue_size, pos_integer()} | {:rate_limits, map()} | {:cursor_secret, binary()} | {:cursor_ttl, pos_integer()} | {:request_state_secret, binary()} | {:request_state_instance, binary()} | {:request_state_store, pid()} | {:clustered, boolean()} | {:request_state_ttl, pos_integer()} | {:scope_map, map()} | {:default_scopes, [String.t()]} | {:registrations, [registration()]} | {:session_store, AttestoMCP.Server.SessionStore.adapter()} | {:session_namespace, String.t()} | {:session_clustered, boolean()} | {:url_elicitation_store, AttestoMCP.Server.UrlElicitationStore.adapter()} | {:url_elicitation_max_entries, pos_integer()} | {:url_elicitation_max_bytes, pos_integer()} | {:telemetry_metadata, map()} | {:exception_reporter, term()} | {:handler_task_init, term()} | {:subscription_timeout, pos_integer()} | {:page_size, pos_integer()} | {:cache_ttl_ms, non_neg_integer()} | {:cache_scope, cache_scope()} | {:allow_public_cache, boolean()} | {:cache_policy, keyword() | map()} | {:max_request_meta_bytes, pos_integer()} | {:initialize_callback, (map(), map() -> :ok | {:error, term()})} | {:instructions, String.t()} | {:instructions_provider, host_callback()} | {:tool_presentation, host_callback()} | {:server_name, String.t()} | {:server_version, String.t()} | {:server_icons, [icon()]} | {:export_schema_dialect, boolean()} | {:capabilities, map()} | {:modern_tasks, false} | {:legacy_tasks, false}
A supported server startup option and its value.
@type server_opts() :: [server_option()]
Keyword options accepted by start_link/1; task flags are disabled in this release.
Modern subscription category/resource filters.
@type tool_result() :: map()
A normalized tool result with content and optional structured output.
Functions
@spec active_session_ids( server(), keyword() ) :: {:ok, %{session_ids: [String.t()], next_cursor: String.t() | nil}} | {:error, :invalid_options | :session_store_unavailable | :unsupported}
Returns one bounded page of active legacy session IDs for operator tooling.
Cancels a request owned by the supplied principal binding.
When the transport configures :principal_binding, pass the derived binding
rather than the complete loaded principal. Otherwise pass the principal used
when the request started.
Cancels a modern subscription.
Cancels a modern subscription owned by the given sink process.
@spec child_spec(server_opts()) :: Supervisor.child_spec()
Returns a supervision child spec keyed by the optional registered server name.
Closes a modern subscription.
Closes a modern subscription owned by the given sink process.
@spec consume_url_elicitation(server(), String.t(), term()) :: {:ok, %{action: String.t(), fields: map(), expires_at_ms: non_neg_integer()}} | {:error, :not_found | :foreign | :consumed | :expired | :store_unavailable}
Atomically consumes a staged URL elicitation record upon confirmation.
Deletes a legacy session and its owned streams.
@spec dispatch(server(), request(), handler_context(), keyword()) :: term()
Dispatches one decoded request through the shared protocol core.
Looks up a session only for its principal binding and tenant.
Creates a principal-binding/tenant-bound legacy session.
Returns normalized startup options used by Plug and stdio adapters.
@spec publish(server(), map(), [publish_option()]) :: :ok | {:error, term()}
Queues a filtered modern notification and publishes its legacy event.
An optional :authorize callback is combined with each stream's captured
delivery authorization for both modern and legacy subscribers. Only a
literal true permits delivery; callback failures suppress it. Its context
keeps required_scopes as the primary all-of clause and adds
required_scope_sets with every accepted clause.
@spec register(server(), atom(), String.t(), definition()) :: :ok | {:error, term()}
Registers one primitive of a supported type.
@spec register_all(server(), [registration()]) :: :ok | {:error, term()}
Atomically registers a bounded primitive batch with coalesced invalidations.
@spec register_completion(server(), String.t(), definition()) :: :ok | {:error, term()}
Registers a completion handler tied to an explicit prompt/template reference.
@spec register_prompt(server(), String.t(), definition()) :: :ok | {:error, term()}
Registers a prompt definition, including required and optional arguments.
The handler input is %{name: name, arguments: arguments}. A definition may
match an argument directly when it declares that argument as required, for
example:
handler: fn %{arguments: %{"topic" => topic}}, _context ->
{:ok, [%{"role" => "user", "content" => %{"type" => "text", "text" => topic}}]}
endUse Map.get(arguments, "topic") instead when the argument is optional.
@spec register_resource(server(), String.t(), definition()) :: :ok | {:error, term()}
Registers a static resource definition.
@spec register_resource_template(server(), String.t(), definition()) :: :ok | {:error, term()}
Registers a URI-template resource definition.
@spec register_tool(server(), String.t(), definition()) :: :ok | {:error, term()}
Registers a tool definition and publishes a modern catalog invalidation.
@spec replace_catalog(server(), [registration()]) :: :ok | {:error, term()}
Atomically replaces the complete primitive catalog from one bounded batch.
@spec resolve_url_elicitation(server(), String.t(), term()) :: {:ok, %{action: String.t(), fields: map(), expires_at_ms: non_neg_integer()}} | {:error, :not_found | :foreign | :consumed | :expired | :store_unavailable}
Resolves a staged URL elicitation record for the host's approval page.
Returns the registered primitive snapshot.
@spec stage_url_elicitation(server(), map(), String.t(), map(), keyword()) :: {:ok, %{id: String.t(), expires_at_ms: non_neg_integer()}} | {:error, term()}
Stages a URL elicitation record before returning a mode: "url" input request.
@spec start_link(server_opts()) :: GenServer.on_start()
Starts the supervised, transport-neutral MCP server.
iex> {:ok, server} = AttestoMCP.Server.API.start_link([])
iex> is_pid(server)
true
iex> GenServer.stop(server)
:ok
Returns bounded public counters for active work and transport state.