ConduitMcp.Protocol.Modern (ConduitMCP v0.11.0)

Copy Markdown View Source

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-scoped io.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/call never echoes the request's _meta on this era, so these are the tool's own;
  • on the six cacheable results — server/discover, tools/list, prompts/list, resources/list, resources/templates/list and resources/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

Types

Cache hints: a TTL in milliseconds and a scope.

Options for to_response/4

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

cache()

@type cache() :: %{ttl_ms: non_neg_integer(), scope: :private | :public}

Cache hints: a TTL in milliseconds and a scope.

option()

@type option() :: {:server_info, map()} | {:cache, cache() | nil}

Options for to_response/4:

  • :server_info — %{"name" => ..., "version" => ...}, put into the result's _meta; omitted when absent.
  • :cache — a cache/0 overriding the configured default for this response (cacheable methods only); nil means the default.

Functions

cacheable_methods()

@spec cacheable_methods() :: [String.t()]

The methods whose complete results carry ttlMs and cacheScope.

default_cache()

@spec default_cache() :: cache()

The configured default cache hints (:cache_ttl_ms, :cache_scope).

http_status(code)

@spec http_status(map() | integer()) :: 200 | 400 | 404

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).

resource_not_found()

@spec resource_not_found() :: integer()

The modern era's "resource not found" code, -32602.

to_response(id, method, result, opts \\ [])

@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"}}