Snodo.Sampling (snodo v0.4.1)

Copy Markdown View Source

Builds and validates embedded sampling/createMessage requests for MRTR.

SEP-2577 deprecates server-initiated sampling in MCP 2026-07-28. The method is still defined by the protocol schema and scored by the official conformance runner, so a 2026-07-28 handler may return one as an input request through Snodo.Result.input_required/1, next to elicitation. Prefer elicitation for new designs; sampling stays available for clients that declare the sampling capability.

create_message/2 returns a bare input request, without a JSON-RPC envelope. Messages are Snodo.Prompt.message/2 maps whose content is one text, image, audio, tool-use, or tool-result block, or a list of those blocks. Use response/3 on a retried request to read only the named CreateMessageResult. It does not bind a response to a user or persist state: applications must do that using authenticated context and verified, request-specific state.

The dialect refuses the request with -32021 when the client has not declared sampling, sampling.tools (needed by :tools and :tool_choice), or sampling.context (needed by an :include_context other than "none"). Sampled content is model output: treat it as untrusted input, never as an instruction or a verified fact.

Summary

Functions

Builds a sampling input request, raising ArgumentError when invalid.

Reads and validates the named response, ignoring unrelated response IDs.

Checks the request against this request's client capabilities.

Checks that response is a CreateMessageResult.

Validates one bare sampling input request.

Types

request()

@type request() :: %{required(String.t()) => term()}

response_result()

@type response_result() :: :missing | {:ok, map()} | {:error, Snodo.Error.t()}

Functions

create_message(messages, opts)

@spec create_message([map()], keyword()) :: request()

Builds a sampling input request, raising ArgumentError when invalid.

messages is a non-empty list of sampling messages. Options:

  • :max_tokens - required, a positive integer.
  • :system_prompt - a string.
  • :model_preferences - a map with optional "hints" (a list of %{"name" => string} maps) and "costPriority", "speedPriority", and "intelligencePriority" numbers from 0 to 1.
  • :temperature - a number.
  • :stop_sequences - a list of strings.
  • :include_context - "none", "thisServer", or "allServers". The last two are deprecated by SEP-2596 and need sampling.context.
  • :metadata - a JSON object passed through to the client's provider.
  • :tools - a list of tool definitions in wire shape; needs sampling.tools.
  • :tool_choice - %{"mode" => "auto" | "none" | "required"}; needs sampling.tools.

response(context, id, request)

@spec response(Snodo.Context.t() | map(), String.t(), request()) :: response_result()

Reads and validates the named response, ignoring unrelated response IDs.

A valid response is a CreateMessageResult: a "role", one sampling content block or a list of them as "content", a "model" string, and an optional "stopReason". Invalid responses return a generic invalid-params error without the submitted data. Additional JSON fields are preserved and ignored. This helper does not authenticate content or trust client-echoed request state.

supported?(request, arg2)

@spec supported?(term(), term()) :: boolean()

Checks the request against this request's client capabilities.

Requires a sampling object, plus sampling.tools when the request carries tools or toolChoice and sampling.context when includeContext is not "none".

valid_response?(response)

@spec valid_response?(term()) :: boolean()

Checks that response is a CreateMessageResult.

That is a "role" of "user" or "assistant", one sampling content block or a non-empty list of them as "content", a "model" string, and an optional "stopReason" string. Additional JSON fields are allowed. Snodo.Client applies it to what a sampling handler returns.

validate_request(request)

@spec validate_request(term()) :: :ok | {:error, String.t()}

Validates one bare sampling input request.