A local GenServer that stands in for a GenServer behind an MCP endpoint.
Application code keeps GenServer.call/3 and GenServer.cast/2; each
becomes a tools/call chosen by the contract.
Requests go out one at a time, in the order the proxy receives them, and each waits for its result before the next is sent. That keeps the ordering OTP gives a sender and a GenServer: casts arrive in order, and a call sees every cast sent before it. The remote GenServer handles one message at a time anyway; the cost is that requests do not overlap on the wire.
Failure semantics follow OTP where the wire allows it:
- The caller's own
GenServer.calltimeout decides how long the caller waits, and a result that arrives after the caller gave up is dropped, as a late reply is in OTP. The request itself is not cancelled: it holds the single in-flight slot until the remote side answers or:request_timeoutexpires, so later requests wait behind it. - A call the remote side timed out (the adapter's
:timeout, or the proxy's:request_timeout, default 60,000 ms) is answered{:error, :timeout}, so a caller with an:infinitytimeout is not left waiting. - A call to a remote target that is not there (
GenServer target unavailable) stops the proxy with:noproc, so aProcess.monitor/1on the proxy deliversDOWNand a supervisor can restart it. A cast to it is dropped, as a cast to a dead name is in OTP. Withon_unavailable: :errorthe call is answered{:error, :noproc}instead and the proxy keeps running. - At most
:max_queuerequests (default 1,000) wait behind the one in flight. A call beyond that is answered{:error, :queue_full}and a cast is dropped. - A request whose process crashes, for example on arguments JSON cannot
encode, is answered
{:error, {:request_failed, reason}}, and later requests still run. - A reply
decode_reply/2does not match is answered{:error, {:decode_failed, name, json}}.encode_call/1andencode_cast/1run on the caller's message in the proxy, as a GenServer callback does, so a raise there stops the proxy. - A connection failure stops the proxy with
{:nodedown, url}. An HTTP error status, such as 401 or 404, is answered{:error, error}and the proxy keeps running. - A message the contract does not know stops the proxy with
{:function_clause, message}, which is what an unmatched callback clause does to a GenServer.
Options: :url and :contract (required), :name, :headers,
:request_timeout, :max_queue, :on_unavailable (:stop, the default,
or :error), and :connect, a keyword list merged over the
defaults passed to Snodo.Client.connect/2 (protocol: "2026-07-28",
timeout: 15_000), for example connect: [token_provider: provider].
What it cannot give: a pid for the remote process, links to it, or any DOWN that is not provoked by a call. Those need a subscription stream.
Summary
Functions
Returns a specification to start this module under a supervisor.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.