Shapes a neutral handler result (ConduitMcp.Handler.result/0) into the
JSON-RPC 2.0 response map of the modern (2026-07-28) protocol era, and
maps modern errors to their HTTP status.
Results
Every successful result gets:
"resultType" => "complete";_meta["io.modelcontextprotocol/serverInfo"]— the server's name and version. Request-scopedio.modelcontextprotocol/*keys a method echoed from the request's_meta(protocol version, client capabilities, client info, log level) are removed from the result's_meta; the result's other keys are kept.tools/callnever echoes the request's_metaon this era, so these are the tool's own;- on the six cacheable results —
server/discover,tools/list,prompts/list,resources/list,resources/templates/listandresources/read—"ttlMs"and"cacheScope".
Cache hints default to the application environment:
config :conduit_mcp, cache_ttl_ms: 60_000, cache_scope: :private(:private or :public; defaults 60_000 and :private). A caller can
override them per response with the :cache option of to_response/4.
{:ok, result} is always a complete result.
Input required
{:input_required, input_requests, request_state} — produced by the
handler once it has gated the input requests and sealed the callback's
state into the request_state token — becomes an InputRequiredResult:
"resultType" => "input_required", "inputRequests", "requestState"
and the serverInfo _meta, but never ttlMs/cacheScope, on any
method (an interim result must not be cached).
Tasks
{:task, task} — a task created for a tools/call by the
io.modelcontextprotocol/tasks extension (ConduitMcp.Ext.Tasks) —
becomes a CreateTaskResult: the Task fields with
"resultType" => "task" and the serverInfo _meta, never
ttlMs/cacheScope.
Errors
Error responses keep the legacy shape. One code is rewritten: -32002
(resource not found in 2025-11-25 and earlier, and the code
handle_read_resource/2 callbacks return) becomes -32602, because
2026-07-28 forbids emitting -32002.
HTTP status
http_status/1 maps a response (or bare code) produced while running a
method: -32020/-32021/-32022 ⇒ 400, -32601 ⇒ 404, everything else
(results, and errors such as an unknown tool's -32602) ⇒ 200. A
rejection raised before the method runs — any ConduitMcp.Era.classify/2
error, including its -32602 for invalid _meta, and any
ConduitMcp.Plugs.HeaderValidation error — is always 400.
Summary
Functions
The methods whose complete results carry ttlMs and cacheScope.
The configured default cache hints (:cache_ttl_ms, :cache_scope).
The HTTP status for a modern response, or for an error code, produced while running a method (see the moduledoc for rejections raised before it).
The modern era's "resource not found" code, -32602.
Builds the modern JSON-RPC response to method request id.
Types
@type cache() :: %{ttl_ms: non_neg_integer(), scope: :private | :public}
Cache hints: a TTL in milliseconds and a scope.
Options for to_response/4:
:server_info—%{"name" => ..., "version" => ...}, put into the result's_meta; omitted when absent.:cache— acache/0overriding the configured default for this response (cacheable methods only);nilmeans the default.
Functions
@spec cacheable_methods() :: [String.t()]
The methods whose complete results carry ttlMs and cacheScope.
@spec default_cache() :: cache()
The configured default cache hints (:cache_ttl_ms, :cache_scope).
The HTTP status for a modern response, or for an error code, produced while running a method (see the moduledoc for rejections raised before it).
@spec resource_not_found() :: integer()
The modern era's "resource not found" code, -32602.
@spec to_response( ConduitMcp.Protocol.json_rpc_id() | nil, String.t() | nil, ConduitMcp.Handler.result(), [option()] ) :: map()
Builds the modern JSON-RPC response to method request id.
Examples
iex> ConduitMcp.Protocol.Modern.to_response(1, "tools/call", {:ok, %{"content" => []}})
%{"jsonrpc" => "2.0", "id" => 1, "result" => %{"content" => [], "resultType" => "complete"}}
iex> ConduitMcp.Protocol.Modern.to_response(1, "resources/read", {:error, -32002, "Resource not found", nil})
%{"jsonrpc" => "2.0", "id" => 1, "error" => %{"code" => -32602, "message" => "Resource not found"}}