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

x402 v2 payment flows over the Model Context Protocol (MCP transport).

This module implements the
[x402 MCP transport](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/mcp.md)
as pure functions over plain MCP tool-call request/result maps, so it works
with any Elixir MCP library (or none). The payment data rides in three
well-known places:

1. A paid tool called without payment returns a tool result with
   `"isError" => true` whose `"structuredContent"` (and JSON-encoded
   `content[0].text`) carry the `PaymentRequired` object.
2. The client retries the tool call with the signed `PaymentPayload` in
   request params `_meta["x402/payment"]`.
3. The server verifies, runs the tool, settles, and attaches the settlement
   receipt to result `_meta["x402/payment-response"]`.

`X402.MCP.Server` wraps a tool handler with verify → execute → settle;
`X402.MCP.Client` drives an arbitrary tool-call function through the
detect → sign → retry-once loop. The helpers here are shared by both halves
and useful on their own when wiring a concrete MCP library.

All functions accept maps with string keys (as decoded from JSON) or atom
keys, but the x402 `_meta` entries themselves always use the spec's string
keys `"x402/payment"` and `"x402/payment-response"`.

## Sign-In-With-X over MCP

The MCP transport spec does not say how a `sign-in-with-x` proof travels;
this library carries the same Base64 value the HTTP `SIGN-IN-WITH-X`
header holds in request `_meta["x402/sign-in-with-x"]` (`put_siwx/2`,
`fetch_siwx/1`), mirroring the payment entry.

## Telemetry

The MCP transport emits the following events, each with `%{count: 1}`
measurements:

- `[:x402, :mcp, :payment_required]` — server advertised payment requirements
- `[:x402, :mcp, :payment_verified]` — server verified and settled a payment
- `[:x402, :mcp, :payment_rejected]` — server rejected a payment
  (metadata includes `:reason`)
- `[:x402, :mcp, :call]` — client drove a tool call
  (metadata includes `:status` and `:paid` or `:reason`)

# `fetch_payment`
*since 0.6.0* 

```elixir
@spec fetch_payment(map()) :: {:ok, map()} | :error
```

Fetches the `PaymentPayload` from a tool-call request's `_meta`.

Performs the same minimal structural check as the upstream SDKs — the value
must be a map with `x402Version` and `payload` — full validation happens
during verification.

## Examples

    iex> payment = %{"x402Version" => 2, "accepted" => %{}, "payload" => %{}}
    iex> request = %{"name" => "search", "_meta" => %{"x402/payment" => payment}}
    iex> X402.MCP.fetch_payment(request)
    {:ok, payment}

    iex> X402.MCP.fetch_payment(%{"name" => "search"})
    :error

# `fetch_payment_required`
*since 0.6.0* 

```elixir
@spec fetch_payment_required(map()) :: {:ok, map()} | :error
```

Fetches the `PaymentRequired` object from a payment-required tool result.

Per the MCP transport spec, the result must have `isError: true`.
`structuredContent` is preferred; `content[0].text` is parsed as JSON when
structured content is absent. Returns `:error` for any other tool result.

## Examples

    iex> payment_required = %{"x402Version" => 2, "error" => "Payment required", "accepts" => []}
    iex> result = %{"isError" => true, "structuredContent" => payment_required, "content" => []}
    iex> X402.MCP.fetch_payment_required(result)
    {:ok, payment_required}

    iex> X402.MCP.fetch_payment_required(%{"content" => [%{"type" => "text", "text" => "hi"}]})
    :error

# `fetch_payment_required_from_error`
*since 0.6.0* 

```elixir
@spec fetch_payment_required_from_error(term()) :: {:ok, map()} | :error
```

Fetches the `PaymentRequired` object from a JSON-RPC error.

Some MCP stacks surface payment challenges as JSON-RPC errors instead of
tool results: code `402` (legacy x402) carries `PaymentRequired` directly in
`data`, and code `-32042` (SEP-1036 `UrlElicitationRequired`, which MCP
designates for payment/elicitation flows) carries it in `data` or namespaced
under `data.x402`.

## Examples

    iex> payment_required = %{"x402Version" => 2, "accepts" => []}
    iex> error = %{"code" => 402, "message" => "Payment required", "data" => payment_required}
    iex> X402.MCP.fetch_payment_required_from_error(error)
    {:ok, payment_required}

    iex> error = %{
    ...>   "code" => -32042,
    ...>   "message" => "Elicitation required",
    ...>   "data" => %{"x402" => %{"x402Version" => 2, "accepts" => []}}
    ...> }
    iex> X402.MCP.fetch_payment_required_from_error(error)
    {:ok, %{"x402Version" => 2, "accepts" => []}}

    iex> X402.MCP.fetch_payment_required_from_error(%{"code" => -32600})
    :error

# `fetch_payment_response`
*since 0.6.0* 

```elixir
@spec fetch_payment_response(map()) :: {:ok, map()} | :error
```

Fetches the settlement receipt from a tool result's `_meta`.

The receipt must be a map containing `success` (the `SettlementResponse`
schema).

## Examples

    iex> receipt = %{"success" => true, "transaction" => "0xabc", "network" => "eip155:84532"}
    iex> result = %{"content" => [], "_meta" => %{"x402/payment-response" => receipt}}
    iex> X402.MCP.fetch_payment_response(result)
    {:ok, receipt}

    iex> X402.MCP.fetch_payment_response(%{"content" => []})
    :error

# `fetch_siwx`
*since 0.9.0* 

```elixir
@spec fetch_siwx(map()) :: {:ok, String.t()} | :error
```

Fetches the Sign-In-With-X proof from a tool-call request's `_meta`.

Returns the Base64 value as sent; decode it with
`X402.Extensions.SIWX.decode_signed/1`.

## Examples

    iex> request = %{"name" => "search", "_meta" => %{"x402/sign-in-with-x" => "abc"}}
    iex> X402.MCP.fetch_siwx(request)
    {:ok, "abc"}

    iex> X402.MCP.fetch_siwx(%{"name" => "search", "_meta" => %{"x402/sign-in-with-x" => ""}})
    :error

# `payment_meta_key`
*since 0.6.0* 

```elixir
@spec payment_meta_key() :: String.t()
```

Returns the request `_meta` key carrying the client's `PaymentPayload`.

## Examples

    iex> X402.MCP.payment_meta_key()
    "x402/payment"

# `payment_required_result`
*since 0.6.0* 

```elixir
@spec payment_required_result(map()) ::
  {:ok, map()} | {:error, :invalid_payment_required}
```

Builds the payment-required tool result for a `PaymentRequired` object.

Per the MCP transport spec the object is provided in **both** formats:
`structuredContent` carries it directly and `content[0].text` carries the
same object JSON-encoded, with `isError: true`.

Returns `{:error, :invalid_payment_required}` when the map lacks the
`x402Version`/`accepts` structure or cannot be encoded as JSON.

## Examples

    iex> payment_required = %{"x402Version" => 2, "error" => "Payment required", "accepts" => []}
    iex> {:ok, result} = X402.MCP.payment_required_result(payment_required)
    iex> {result["isError"], result["structuredContent"] == payment_required}
    {true, true}
    iex> [%{"type" => "text", "text" => text}] = result["content"]
    iex> Jason.decode!(text) == payment_required
    true

    iex> X402.MCP.payment_required_result(%{"accepts" => []})
    {:error, :invalid_payment_required}

# `payment_response_meta_key`
*since 0.6.0* 

```elixir
@spec payment_response_meta_key() :: String.t()
```

Returns the result `_meta` key carrying the server's settlement receipt.

## Examples

    iex> X402.MCP.payment_response_meta_key()
    "x402/payment-response"

# `put_payment`
*since 0.6.0* 

```elixir
@spec put_payment(map(), map()) :: map()
```

Attaches a `PaymentPayload` to a tool-call request's `_meta`.

Existing `_meta` entries are preserved; when the request uses an atom
`:_meta` key it is kept (avoiding a duplicate key on JSON encoding).

## Examples

    iex> request = %{"name" => "search", "arguments" => %{"q" => "x402"}}
    iex> X402.MCP.put_payment(request, %{"x402Version" => 2, "payload" => %{}})
    %{
      "name" => "search",
      "arguments" => %{"q" => "x402"},
      "_meta" => %{"x402/payment" => %{"x402Version" => 2, "payload" => %{}}}
    }

# `put_payment_response`
*since 0.6.0* 

```elixir
@spec put_payment_response(map(), map()) :: map()
```

Attaches a settlement receipt to a tool result's `_meta`.

## Examples

    iex> result = %{"content" => [%{"type" => "text", "text" => "ok"}]}
    iex> X402.MCP.put_payment_response(result, %{"success" => true})
    %{
      "content" => [%{"type" => "text", "text" => "ok"}],
      "_meta" => %{"x402/payment-response" => %{"success" => true}}
    }

# `put_siwx`
*since 0.9.0* 

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

Attaches a Sign-In-With-X proof (the Base64 `SIGN-IN-WITH-X` header
value) to a tool-call request's `_meta`.

## Examples

    iex> request = %{"name" => "search"}
    iex> X402.MCP.put_siwx(request, "eyJkb21haW4iOiJhcGkuZXhhbXBsZS5jb20ifQ==")
    %{"name" => "search", "_meta" => %{"x402/sign-in-with-x" => "eyJkb21haW4iOiJhcGkuZXhhbXBsZS5jb20ifQ=="}}

# `siwx_meta_key`
*since 0.9.0* 

```elixir
@spec siwx_meta_key() :: String.t()
```

Returns the request `_meta` key carrying a Sign-In-With-X proof.

## Examples

    iex> X402.MCP.siwx_meta_key()
    "x402/sign-in-with-x"

---

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