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/x402Set up your wallet
Before integrating the plugin, you need a wallet address to receive payments:
- Create or use an existing Ethereum-compatible wallet (e.g., Coinbase Wallet, MetaMask)
- Get your wallet's public address (starts with
0x) - For testing, use Base Sepolia testnet and get test USDC from the Coinbase Developer Platform under Wallets > Faucet
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:
Configuration Options
wallet: Your wallet address that receives paymentsfacilitator: (Optional) Facilitator URL (defaults tohttps://x402.org/facilitator)debug: (Optional) Enable debug loggingnano: (Optional) Settings for tools on thenanonetwork, see Nano (XNO)wallet:nano_address that receives XNO (falls back towallet)rpc: Nano node RPC URL used to verify payments (default:https://rpc.nano.to)
defaults: (Optional) Default values for all paid toolsprice: 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:
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.
Tool Options
price: Price in USDC for this tool (falls back to provider default, then 0.01 USDC)network: Override default networkmaxPaymentAge: Override maximum payment agedescription: Description used in payment requirements
Get Payment Details
Access payment details inside your tool using the payment() function:
Networks
The plugin supports three networks:
| Network | ID | Asset | Use Case |
|---|---|---|---|
base | eip155:8453 | USDC | Production with real USDC |
base-sepolia | eip155:84532 | USDC | Testing with testnet USDC |
nano | nano:mainnet | XNO | Production 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.
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
maxPaymentAgeseconds, 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
maxPaymentAgeshort. - 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
maxPaymentAgeseconds) - 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
x402Provideris exported as default frommiddleware.ts - Only call
payment()inside paid tool handlers - Don't call
payment()at module load time
