Skip to content

x402

The x402 plugin enables tool monetization through the HTTP 402 payment protocol using USDC on Base.

For the complete documentation index, see llms.txt. Markdown variants of every page are available by appending .md to the URL.

Overview

The x402 plugin integrates the x402 payment protocol with your xmcp server, enabling you to charge USDC micropayments for tool usage on the Base blockchain.

Installation

Install the x402 plugin:

pnpm add @xmcp-dev/x402

Set up your wallet

Before integrating the plugin, you need a wallet address to receive payments:

  1. Create or use an existing Ethereum-compatible wallet (e.g., Coinbase Wallet, MetaMask)
  2. Get your wallet's public address (starts with 0x)
  3. For testing, use Base Sepolia testnet and get test USDC from the Coinbase Developer Platform under Wallets > Faucet
Info

The x402 protocol uses USDC stablecoin for payments. On Base mainnet, 1 USDC = 1 USD.

Environment Variables

Create a .env file in the root of your project and configure the following environment variables:

Set up the Provider

Create a middleware.ts file in your xmcp app's src directory and import the provider from the package:

src/middleware.ts

Configuration Options

  • wallet: Your wallet address that receives payments
  • facilitator: (Optional) Facilitator URL (defaults to https://x402.org/facilitator)
  • debug: (Optional) Enable debug logging
  • nano: (Optional) Settings for tools on the nano network, see Nano (XNO)
    • wallet: nano_ address that receives XNO (falls back to wallet)
    • rpc: Nano node RPC URL used to verify payments (default: https://rpc.nano.to)
  • defaults: (Optional) Default values for all paid tools
    • price: Price in USDC (default: 0.01)
    • currency: Currency code (default: "USDC")
    • network: Network - "base", "base-sepolia" or "nano" (default: "base")
    • maxPaymentAge: Maximum payment age in seconds (default: 300)

Monetize a Tool

Wrap your tool functions with paid() to require payment:

src/tools/analyze.ts

Pricing

You can set a custom price for each tool by passing a price option to paid(). If not specified, the tool will use the default price from your provider configuration. If the provider doesn't specify a default price either, the tool will cost 0.01 USDC per call.

src/tools/premium-analysis.ts
src/tools/basic-tool.ts

Tool Options

  • price: Price in USDC for this tool (falls back to provider default, then 0.01 USDC)
  • network: Override default network
  • maxPaymentAge: Override maximum payment age
  • description: Description used in payment requirements

Get Payment Details

Access payment details inside your tool using the payment() function:

src/tools/paid-greet.ts

Networks

The plugin supports three networks:

NetworkIDAssetUse Case
baseeip155:8453USDCProduction with real USDC
base-sepoliaeip155:84532USDCTesting with testnet USDC
nanonano:mainnetXNOProduction with real XNO

Nano (XNO)

Tools can also be paid in XNO on the Nano network, next to USDC on Base. Nano has no facilitator: the payer publishes a send block to your Nano address first, then retries the call with the block hash as the payment payload. The plugin checks the block against a Nano node and does not hold a key or broadcast anything.

src/middleware.ts
src/tools/word-count.ts
Warning

On the nano network, price is an amount of XNO, not USD. There is no exchange rate conversion. The payment requirements advertise the amount in raw (1 XNO = 10^30 raw).

The client sends the hash of its send block in the payload of the x402 payment:

A payment is accepted when the block:

  • is a confirmed state block with subtype send
  • sends to the configured nano.wallet (checksum validated)
  • sends at least the tool price (previous balance minus the block's balance)
  • was first seen by the node within maxPaymentAge seconds, when the node reports a timestamp
  • has not already paid for another call on this server

The block hash and payer are returned in payment() and in the x402/payment-response metadata. If the tool returns an error, the block can be used again for a retry.

Things to know before using it:

  • The block hash is a bearer proof. Anyone who sees the block on the public ledger could present it first, so keep maxPaymentAge short.
  • Used block hashes are kept in memory. They are forgotten on restart and are not shared between server instances.
  • Verification depends on the Nano node RPC you configure. Use a node you trust.

Troubleshooting

Payment Required Response

If clients receive a 402 response, they need to:

  • Extract payment requirements from result.structuredContent.accepts
  • Sign a payment authorization using their wallet
  • Retry with payment in params._meta["x402/payment"]

Payment Verification Failed

If payment verification fails:

  • Ensure the payment amount matches the tool price
  • Check that the payment is recent (within maxPaymentAge seconds)
  • Verify the correct network is being used (base vs base-sepolia)
  • For nano, ensure the send block is confirmed before retrying the call

Settlement Errors

If settlement fails after tool execution:

  • Check the facilitator URL is correct and reachable
  • Verify your wallet address is valid
  • Ensure the payer has sufficient USDC balance

Missing Payment Context

If payment() throws "x402 context not initialized":

  • Ensure x402Provider is exported as default from middleware.ts
  • Only call payment() inside paid tool handlers
  • Don't call payment() at module load time

On this page

One framework to rule them all