Sans-IO MCP session core, shared by server sessions and client connections.
Both ends of MCP are symmetric JSON-RPC peers; this module models one end as a
pure state machine. The owning process feeds decoded Noizu.MCP.JsonRpc
messages through ingest/2 and interprets the returned effects — Peer
itself never touches a socket or process.
Effects:
{:send, message}— encode and writemessageto the transport{:dispatch, method, id, params}— an inbound request for the feature layer{:notice, method, params}— an inbound notification for the feature layer{:resolve, tag, id, result}— an outbound request completed (result :: {:ok, map} | {:error, Noizu.MCP.Error.t()}){:cancel_in, id, reason}— the remote cancelled a request we are processing{:progress, tag, id, params}— progress for one of our outbound requests{:ready, remote_info}— handshake complete, normal traffic may flow{:initialize_result, result}— (client) the initialize response arrived; follow withinitialized/1{:initialize_failed, reason}— (client) the server negotiated an unsupported version
Summary
Functions
Abandon an outbound request and build the notifications/cancelled to send.
Any late response for it is silently ignored. Returns the abandoned
request's tag (or nil if the id was unknown).
Process one decoded inbound message; returns {peer, effects}.
(client) Build the initialize request.
(client) Complete the handshake: build notifications/initialized.
Build a new peer.
Build a notification struct (stateless helper).
Issue an outbound request. Returns the assigned id and the request struct to
send. opts: :tag (returned in the :resolve effect), :progress_token
(adds _meta.progressToken and routes inbound progress notifications).
Respond to an inbound request. Returns :drop when the request was cancelled
by the remote (the spec forbids responding after cancellation).
Respond to an inbound request with a protocol error. See respond/3.
Types
@type effect() :: tuple()
@type phase() :: :handshake | :initializing | :ready | :closing
@type role() :: :server | :client
@type t() :: %Noizu.MCP.Peer{ cancelled_in: MapSet.t(), instructions: String.t() | nil, local_capabilities: map(), local_info: Noizu.MCP.Types.Implementation.t(), next_id: pos_integer(), pending_in: %{optional(Noizu.MCP.JsonRpc.id()) => String.t()}, pending_out: %{optional(Noizu.MCP.JsonRpc.id()) => map()}, phase: phase(), progress_index: %{optional(term()) => Noizu.MCP.JsonRpc.id()}, protocol_version: String.t() | nil, remote_capabilities: map() | nil, remote_info: Noizu.MCP.Types.Implementation.t() | nil, role: role() }
@type tag() :: term()
Functions
@spec cancel_out(t(), Noizu.MCP.JsonRpc.id(), String.t() | nil) :: {t(), Noizu.MCP.JsonRpc.Notification.t() | nil, tag() | nil}
Abandon an outbound request and build the notifications/cancelled to send.
Any late response for it is silently ignored. Returns the abandoned
request's tag (or nil if the id was unknown).
@spec ingest(t(), Noizu.MCP.JsonRpc.message()) :: {t(), [effect()]}
Process one decoded inbound message; returns {peer, effects}.
@spec init_request(t()) :: {t(), Noizu.MCP.JsonRpc.Request.t()}
(client) Build the initialize request.
@spec initialized(t()) :: {t(), Noizu.MCP.JsonRpc.Notification.t(), [effect()]}
(client) Complete the handshake: build notifications/initialized.
Build a new peer.
Options: :role (:server | :client, required), :info
(Noizu.MCP.Types.Implementation, required), :capabilities (wire-format
map), :instructions (server only).
@spec notification(String.t(), map() | nil) :: Noizu.MCP.JsonRpc.Notification.t()
Build a notification struct (stateless helper).
@spec request(t(), String.t(), map() | nil, keyword()) :: {t(), Noizu.MCP.JsonRpc.id(), Noizu.MCP.JsonRpc.Request.t()}
Issue an outbound request. Returns the assigned id and the request struct to
send. opts: :tag (returned in the :resolve effect), :progress_token
(adds _meta.progressToken and routes inbound progress notifications).
@spec respond(t(), Noizu.MCP.JsonRpc.id(), map()) :: {t(), {:ok, Noizu.MCP.JsonRpc.Response.t()} | :drop}
Respond to an inbound request. Returns :drop when the request was cancelled
by the remote (the spec forbids responding after cancellation).
@spec respond_error(t(), Noizu.MCP.JsonRpc.id(), Noizu.MCP.Error.t()) :: {t(), {:ok, Noizu.MCP.JsonRpc.ErrorResponse.t()} | :drop}
Respond to an inbound request with a protocol error. See respond/3.