Client half of the x402 MCP transport: pay for tool calls automatically.
call/3 drives an arbitrary tool-call function (any MCP client library, or
a plain function in tests) through the x402 detect → sign → retry-once loop:
- Perform the tool call. A result that is not payment-required is returned as-is.
- Extract the
PaymentRequiredobject from the payment-required tool result (or from a402/-32042JSON-RPC error). - Invoke the
:on_payment_requiredhook, which may cancel. - Build and sign a payment via
X402.Client.build_payment/3and retry the tool call once with the payload in request_meta["x402/payment"]. - Return the retried result with the decoded settlement receipt from
result
_meta["x402/payment-response"], when present.
A tool call is never paid twice: at most one payment retry is made, a
second payment-required result is returned as-is, and requests that already
carry _meta["x402/payment"] are refused.
Example
{:ok, signer} = X402.Signer.LocalKey.new(System.fetch_env!("PAYER_KEY"))
request = %{"name" => "premium_search", "arguments" => %{"query" => "x402"}}
{:ok, %{result: result, payment_response: receipt, paid: true}} =
X402.MCP.Client.call(request, &MyMCP.call_tool/1,
signer: signer,
max_amount: "10000",
on_payment_required: fn payment_required ->
IO.inspect(payment_required["accepts"], label: "about to pay")
:ok
end
)The tool-call function receives the (possibly payment-carrying) request map
and may return the tool result map directly, {:ok, result}, or
{:error, reason}.
Summary
Functions
Builds the request _meta map paying for a payment-required response.
Performs a tool call, paying for the tool if it requires payment.
Types
@type call_error() :: :payment_already_attempted | :payment_cancelled | :invalid_tool_result | {:transport_error, term()} | X402.Client.build_error()
A tool-call function driven by call/3.
A completed tool call.
:result is the final tool result map; :payment_response holds the
decoded settlement receipt from _meta["x402/payment-response"] when the
server sent one, otherwise nil; :paid tells whether a payment was
signed and submitted.
Functions
@spec build_payment_meta(map(), X402.Signer.t(), keyword()) :: {:ok, %{required(String.t()) => map()}} | {:error, X402.Client.build_error()}
Builds the request _meta map paying for a payment-required response.
Accepts either a decoded PaymentRequired map or the payment-required tool
result that carries one, selects and signs a payment option via
X402.Client.build_payment/3, and returns the _meta entries to merge into
the retried tool-call request. Options are forwarded to
X402.Client.build_payment/3.
Use this instead of call/3 when your MCP library exposes request _meta
but you want to drive the retry yourself.
@spec call(map(), call_fun(), keyword()) :: {:ok, response()} | {:error, call_error()}
Performs a tool call, paying for the tool if it requires payment.
See the module documentation for the full flow. Returns {:ok, response()}
with the final tool result, or {:error, reason} when the payment was
cancelled, could not be built, or the tool-call function failed.
Options
:signer- Required. A struct implementingX402.Signer, used to sign the payment.:network(String.t/0) - Payment selection filter — seeX402.Client.select_requirements/2.:scheme(String.t/0) - Payment selection filter — seeX402.Client.select_requirements/2.:asset(String.t/0) - Payment selection filter — seeX402.Client.select_requirements/2.:max_amount- Maximumamount(atomic units) this client will pay — the budget guard for automated payers. Requirements above it are never selected.:valid_after_buffer(non_neg_integer/0) - Clock-skew buffer for the authorization'svalidAfter, in seconds. The default value is60.:on_payment_required- Budget/consent hook invoked with the decodedPaymentRequiredmap before any payment is signed. Return:cancelto abort with{:error, :payment_cancelled}; any other return value continues. The default value isnil.