# `X402.MCP.Server`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/mcp/server.ex#L1)

Server half of the x402 MCP transport: gates a tool handler behind payment.

`call/3` inspects an MCP tool-call request for a payment in
`_meta["x402/payment"]` and drives the full verify → execute → settle flow
against an `X402.Facilitator`:

1. No payment → the payment-required tool result (`isError: true` with the
   `PaymentRequired` object in `structuredContent` and `content[0].text`).
2. Invalid payment (wrong version, `accepted` not matching the advertised
   requirements, extension echo mismatch, failed verification) → the same
   payment-required result with the rejection reason.
3. Valid payment → the wrapped handler runs; on success the payment is
   settled and the receipt is attached to result
   `_meta["x402/payment-response"]`. When settlement fails after execution,
   only the payment error is returned — never the tool's content.

The module is MCP-library agnostic: requests and results are plain maps in
the shapes MCP libraries already use, so the wrapper drops into any tool
dispatch function. See the [MCP guide](mcp.html) for integration snippets.

## Example

    config =
      X402.MCP.Server.init(
        tool: "premium_search",
        accepts: [
          %{
            price: "10000",
            network: "eip155:84532",
            asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
            pay_to: "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
            extra: %{"name" => "USDC", "version" => "2"}
          }
        ],
        facilitator: MyApp.Facilitator
      )

    X402.MCP.Server.call(request, config, fn _request ->
      %{"content" => [%{"type" => "text", "text" => "results..."}]}
    end)

## Replay protection

Pass `payment_identifier_cache:` (an
`X402.Extensions.PaymentIdentifier.ETSCache` server, the same option
`X402.Plug.PaymentGate` takes) to atomically claim each payment proof before
settlement, rejecting concurrent or repeated submissions of the same signed
payment. The claim is released when the handler fails or settlement fails,
so the client may retry with the same payment.

## Payment identifier

Advertise the
[`payment-identifier` extension](https://github.com/x402-foundation/x402/blob/main/specs/extensions/payment_identifier.md)
with `extensions: %{"payment-identifier" => X402.Extensions.PaymentIdentifier.extension(required: true)}`.
The id echoed under `extensions["payment-identifier"]["info"]["id"]` must
be 16–128 characters of `[A-Za-z0-9_-]` (otherwise the payment-required
result carries `invalid_payload`); with `required: true` a missing id
yields `payment_identifier_required`. When a cache is configured, the id
is bound to a fingerprint of the matched requirements and the tool name
(`X402.Extensions.PaymentIdentifier.fingerprint/2`) under a `"pid:"` key:
reusing it for a different request yields `payment_identifier_conflict`,
the same request proceeds normally. The pre-0.7.0 `"paymentIdentifier"`
format is still accepted but deprecated (removed in 1.0.0) and emits
`[:x402, :payment_identifier, :legacy]`.

## Builder code

A payment echoing the
[`builder-code` extension](https://github.com/x402-foundation/x402/blob/main/specs/extensions/builder_code.md)
— whether or not `extensions` advertises it with
`X402.Extensions.BuilderCode.extension/2` — is checked with
`X402.Extensions.BuilderCode.validate_echo/2`: malformed codes or more
than ten service codes yield `invalid_payload`, and an app code that
differs from the advertised one is an extension echo mismatch. The codes
travel to the facilitator inside the payload.

## Lifecycle hooks

The `:hooks` module may define the optional resource-server callbacks of
`X402.Hooks`. `c:X402.Hooks.on_protected_request/2` runs on every call
with an `X402.Hooks.RequestContext` (`transport: :mcp`, the request, the
tool name, and the advertised requirements and extensions) before the
payment is inspected: it may continue with replaced requirements or
extensions, halt with `{:halt, {status, body}}` (answered as an
`isError` result carrying `body` in `structuredContent`), or halt with
`{:halt, :skip_payment}` to run the handler unpaid (emitting
`[:x402, :mcp, :pass_through]` with `reason: :hook_skipped`).
`c:X402.Hooks.on_verified_payment_canceled/2` runs when a verified
payment is not settled: the handler returned an error result
(`reason: :handler_failed`), the handler raised or threw
(`reason: :handler_raised`, with `:error`), or settlement failed
(`reason: :settlement_failed`, with `:error`).

# `handler`

```elixir
@type handler() :: (map() -&gt;
                map() | {:ok, map(), non_neg_integer()} | {:error, term()})
```

An MCP tool-call handler: request params in, tool result map out.

# `options`

```elixir
@type options() :: %{
  auth_capture_resource: X402.AuthCapture.Resource.t() | nil,
  tool: String.t(),
  facilitator: X402.Facilitator.server(),
  hooks: module(),
  payment_identifier_cache:
    X402.Extensions.PaymentIdentifier.ETSCache.server() | nil,
  accepts: [map()],
  resource: map(),
  extensions: map()
}
```

Configuration map produced by `init/1`.

# `call`
*since 0.6.0* 

```elixir
@spec call(map(), options(), handler()) :: map()
```

Gates an MCP tool-call request behind x402 payment verification.

`request` is the tool-call params map (typically with `"name"`,
`"arguments"`, and `"_meta"` keys). `handler` receives the request and must
return a tool result map (with a `"content"` list and optional
`"isError"`); it only runs after the payment has been verified.

Always returns a tool result map:

- the payment-required result when payment is missing, invalid, rejected
  by the facilitator, or already settled (replay)
- the handler's result with the settlement receipt attached to
  `_meta["x402/payment-response"]` on success
- the handler's error result unchanged (no settlement) when the handler
  sets `"isError" => true`
- the settlement-failure result (payment-required format, without the
  tool's content) when settlement fails after execution
- an opaque internal error result when the facilitator transport fails

If the handler raises, the replay claim is released and the exception is
re-raised for the MCP library to surface.

# `init`
*since 0.6.0* 

```elixir
@spec init(keyword()) :: options()
```

Validates and compiles paid-tool options.

Raises `NimbleOptions.ValidationError` for invalid options and
`ArgumentError` when `:accepts` is empty, an accept's `extra.paymentFlow`
is not `"authorization"`, or the advertised data cannot be encoded as JSON.

## Options

* `:auth_capture_resource` - Local escrow resource. Paid handlers return `{:ok, result_map, actual_amount}`
  or `{:error, reason}`. Content is withheld until sync settlement or durable
  deferred metering. Recovery is application-only through the resource. The default value is `nil`.

* `:tool` (`t:String.t/0`) - Required. Tool name; used for the default `mcp://tool/{tool}` resource URL.

* `:accepts` (list of `t:map/0`) - Required. Payment options advertised in `PaymentRequired.accepts` (at least one).

* `:facilitator` (`t:term/0`) - Facilitator server pid/name used for verification and settlement. The default value is `X402.Facilitator`.

* `:hooks` - Lifecycle hook module implementing `X402.Hooks`. The default value is `X402.Hooks.Default`.

* `:payment_identifier_cache` - Optional idempotency cache: an `ETSCache` server pid/name (the default
  adapter), or a `{module, cache}` adapter tuple implementing
  `X402.Extensions.PaymentIdentifier.Cache`. When set, the wrapper
  performs an atomic claim (via `put_new`) on a hash of the signed scheme
  payload before settling, preventing concurrent requests from
  double-settling the same payment. The default value is `nil`.

* `:resource_url` - Custom ResourceInfo.url (defaults to `mcp://tool/{tool}`). The default value is `nil`.

* `:description` - ResourceInfo.description (defaults to `Tool: {tool}`). The default value is `nil`.

* `:mime_type` (`t:String.t/0`) - ResourceInfo.mimeType. The default value is `"application/json"`.

* `:service_name` - ResourceInfo.serviceName (printable ASCII, max 32 characters recommended). The default value is `nil`.

* `:tags` (list of `t:String.t/0`) - ResourceInfo.tags (max 5 recommended). The default value is `[]`.

* `:icon_url` - ResourceInfo.iconUrl (absolute http(s) URL). The default value is `nil`.

* `:extensions` - Protocol extensions advertised in PaymentRequired.extensions. The default value is `%{}`.

### Accept option fields (inside `:accepts`)

* `:scheme` - Payment scheme (`exact` or `upto`). The default value is `"exact"`.

* `:price` - Required. Payment amount in atomic token units (PaymentRequirements `amount`).

* `:network` (`t:String.t/0`) - Required. Blockchain network in CAIP-2 format (for example `eip155:84532`).

* `:asset` (`t:String.t/0`) - Required. Token contract address or asset identifier.

* `:pay_to` (`t:String.t/0`) - Required. Recipient wallet address (`payTo` in the PaymentRequirements schema).

* `:max_timeout_seconds` (`t:pos_integer/0`) - Maximum time allowed for payment completion. The default value is `60`.

* `:extra` - Scheme-specific extra fields (string or atom keys). The default value is `%{}`.

# `payment_required_result`
*since 0.6.0* 

```elixir
@spec payment_required_result(options(), String.t()) :: map()
```

Builds the payment-required tool result advertised by this configuration.

Useful for advertising the price of a paid tool outside `call/3` (for
example in a `tools/list` response or documentation).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
