CLAUDE.md•33.4 kB
# CLAUDE.md - X402 + MCP Integration Guide for AI Developers
**Comprehensive reference for AI agents consuming X402-protected APIs via Model Context Protocol**
This document explains how MCP servers integrate with the X402 micropayment protocol to enable AI agents like Claude to seamlessly consume paid APIs without manual payment flows.
---
## Table of Contents
1. [Protocol Overview](#protocol-overview)
2. [MCP + X402 Architecture](#mcp--x402-architecture)
3. [Payment Flow Deep Dive](#payment-flow-deep-dive)
4. [Implementation Patterns](#implementation-patterns)
5. [Tool Development Guide](#tool-development-guide)
6. [Error Handling](#error-handling)
7. [Demo vs Payment Mode](#demo-vs-payment-mode)
8. [Service Discovery](#service-discovery)
9. [Security Considerations](#security-considerations)
10. [FAQ](#faq)
---
## Protocol Overview
### What is X402?
**X402** is an HTTP-based micropayment protocol using cryptographic signatures for API access:
- **Protocol**: HTTP 402 Payment Required with EIP-712 signatures
- **Currency**: USDC (stablecoin)
- **Networks**: Base (mainnet), Base Sepolia (testnet)
- **Gas Fees**: None - facilitator pays gas via EIP-3009
- **Signature**: EIP-712 typed data for payment authorization
- **Cost**: Typically $0.001 - $0.01 per API request
### What is MCP?
**MCP (Model Context Protocol)** is a standard for connecting AI models to external tools:
- **Purpose**: Enable AI agents to access real-world data and services
- **Transport**: JSON-RPC over stdio (stdin/stdout)
- **Tools**: Callable functions with defined schemas
- **Clients**: Claude Desktop, custom MCP clients
- **Servers**: Node.js, Python, or other language implementations
### Why Combine MCP + X402?
**Problem**: AI agents need paid API access but can't handle manual payment flows
**Solution**: MCP + X402 integration provides:
- ✅ **Automatic Payments**: Payment handling abstracted from AI agent
- ✅ **Gasless Transactions**: No blockchain gas fees for users
- ✅ **Type-Safe Tools**: Structured input/output schemas
- ✅ **Seamless UX**: AI makes API calls, payments happen transparently
- ✅ **Production Ready**: Works with real money on Base mainnet
---
## MCP + X402 Architecture
### System Components
```
┌──────────────────────────────────────────────────────────────┐
│ Layer 1: AI Agent (Claude Desktop) │
│ │
│ - User interacts with AI │
│ - AI decides to call MCP tools │
│ - MCP protocol handles tool invocation │
└──────────────────────────────────────────────────────────────┘
↓ MCP JSON-RPC
┌──────────────────────────────────────────────────────────────┐
│ Layer 2: MCP Server (This Template) │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Tool Definitions │ │
│ │ - inputSchema validation │ │
│ │ - outputSchema formatting │ │
│ │ - Tool descriptions for AI │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Payment Client (x402-axios) │ │
│ │ - EIP-712 signature generation │ │
│ │ - Automatic 402 retry logic │ │
│ │ - Payment header construction │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
↓ HTTP + X402
┌──────────────────────────────────────────────────────────────┐
│ Layer 3: X402 API Server │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Payment Middleware (x402-express) │ │
│ │ - 402 response generation │ │
│ │ - Payment verification │ │
│ │ - Facilitator coordination │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Business Logic │ │
│ │ - Process requests │ │
│ │ - Return data │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
↓ USDC Transfer
┌──────────────────────────────────────────────────────────────┐
│ Layer 4: Facilitator + Blockchain │
│ │
│ - Verify EIP-712 signature │
│ - Execute USDC transferWithAuthorization (EIP-3009) │
│ - Pay gas fees on behalf of user │
│ - Return settlement proof │
└──────────────────────────────────────────────────────────────┘
```
### Data Flow Example
**User Request**: "Search for coffee shops near me"
```typescript
// 1. Claude calls MCP tool
CallToolRequest {
name: "search_places",
arguments: {
query: "coffee shops",
location: "San Francisco"
}
}
// 2. MCP server makes HTTP request (first attempt)
POST https://api.example.com/api/places/search
Body: { query: "coffee shops", location: "San Francisco" }
// 3. API returns 402 Payment Required
HTTP 402 Payment Required
{
"x402Version": 1,
"error": "Payment required",
"accepts": [{
"scheme": "exact",
"network": "base-sepolia",
"maxAmountRequired": "1000", // 0.001 USDC (6 decimals)
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"payTo": "0xAPI_WALLET_ADDRESS",
"resource": "https://api.example.com/api/places/search",
"maxTimeoutSeconds": 60,
"extra": {
"name": "USDC",
"version": "2"
}
}]
}
// 4. x402-axios generates EIP-712 signature
const signature = await account.signTypedData({
domain: {
name: "USDC",
version: "2",
chainId: 84532, // Base Sepolia
verifyingContract: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
},
types: {
TransferWithAuthorization: [
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "validAfter", type: "uint256" },
{ name: "validBefore", type: "uint256" },
{ name: "nonce", type: "bytes32" }
]
},
primaryType: "TransferWithAuthorization",
message: {
from: "0xUSER_WALLET",
to: "0xAPI_WALLET_ADDRESS",
value: "1000",
validAfter: "0",
validBefore: Math.floor(Date.now() / 1000) + 60,
nonce: "0x..."
}
});
// 5. Retry with payment header
POST https://api.example.com/api/places/search
Headers: {
"X-PAYMENT": "base64_encoded_payment_data"
}
Body: { query: "coffee shops", location: "San Francisco" }
// 6. API verifies payment and returns data
HTTP 200 OK
Headers: {
"X-PAYMENT-RESPONSE": "base64_encoded_settlement_proof"
}
Body: {
"results": [
{ "name": "Blue Bottle Coffee", ... },
{ "name": "Philz Coffee", ... }
]
}
// 7. MCP server returns to Claude
CallToolResponse {
content: [{
type: "text",
text: JSON.stringify({
success: true,
payment_processed: true,
data: { results: [...] },
metadata: {
cost: "$0.001",
network: "base-sepolia"
}
})
}]
}
// 8. Claude presents results to user
"I found 2 coffee shops near you:
1. Blue Bottle Coffee - ...
2. Philz Coffee - ..."
```
**Key Insight**: Steps 3-6 are completely automatic. The AI agent and user never see the payment flow.
---
## Payment Flow Deep Dive
### Facilitator Architecture
X402 uses **facilitators** to handle payment settlement and gas fees:
#### Base Sepolia (Testnet) - URL Interceptor Pattern
```typescript
import { withPaymentInterceptor } from "x402-axios";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const client = withPaymentInterceptor(
axios.create({ baseURL: "https://api.example.com" }),
account // Wallet account for signing
);
// x402-axios automatically:
// 1. Catches 402 responses
// 2. Generates EIP-712 signature
// 3. Sends payment to x402.coinbase.com/facilitator
// 4. Retries original request with payment proof
```
**Facilitator URL**: `https://x402.coinbase.com/facilitator`
- Free to use
- No API credentials required
- Perfect for testnet development
#### Base Mainnet (Production) - CDP Facilitator Pattern
```typescript
// For mainnet, use Coinbase Developer Platform facilitator
// This is handled on the API server side, not in MCP client
// Server-side (not in this template):
import { facilitator } from "@coinbase/x402";
app.use(paymentMiddleware(
walletAddress,
routes,
facilitator // Requires CDP_API_KEY_ID and CDP_API_KEY_SECRET
));
```
**MCP client side remains the same** - x402-axios handles both networks identically.
### EIP-712 Signature Structure
The payment signature authorizes USDC transfer without blockchain transaction submission:
```typescript
// EIP-712 Domain
{
name: "USDC", // Token name
version: "2", // Token version
chainId: 84532, // Base Sepolia: 84532, Base: 8453
verifyingContract: "0x036CbD53842c5426634e7929541eC2318f3dCF7e" // USDC contract
}
// Message to sign (TransferWithAuthorization)
{
from: "0xUSER_WALLET", // Your wallet address
to: "0xAPI_WALLET_ADDRESS", // API server's wallet
value: "1000", // Amount in atomic units (0.001 USDC)
validAfter: "0", // Valid immediately
validBefore: "1234567890", // Expires in 60 seconds
nonce: "0xRANDOM_BYTES32" // Prevents replay attacks
}
// Resulting signature
"0x1234567890abcdef..." // 65 bytes (r, s, v)
```
**Why EIP-712?**
- **Human-Readable**: Users can see what they're signing
- **Replay Protection**: Nonce prevents reuse
- **Time-Limited**: Signature expires after timeout
- **Gas-Free**: No blockchain transaction needed (yet)
### EIP-3009 Gasless Transfer
After signature verification, facilitator executes the actual transfer:
```solidity
// USDC contract function (EIP-3009)
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external
```
**Gas Payment Flow**:
1. User signs EIP-712 message (free, off-chain)
2. Facilitator receives signature
3. Facilitator submits transaction to blockchain
4. **Facilitator pays gas fees** (not user)
5. USDC transferred from user to API wallet
6. Settlement proof returned
**Result**: User only needs USDC balance, no ETH for gas!
---
## Implementation Patterns
### Pattern 1: Basic Tool with Payment
```typescript
// Tool definition
{
name: "get_data",
description: "Fetch data from X402-protected API",
inputSchema: {
type: "object",
properties: {
id: { type: "string", description: "Data ID" }
},
required: ["id"]
}
}
// Tool handler
case "get_data": {
const { id } = args as { id: string };
// Payment-enabled API call (automatic payment handling)
const response = await client.get(`/api/data/${id}`);
return {
content: [{
type: "text",
text: JSON.stringify(response.data, null, 2)
}]
};
}
```
**Key Points**:
- No manual payment code needed
- x402-axios handles 402 responses automatically
- User never sees payment flow
- Works identical to regular API call from AI perspective
### Pattern 2: POST Request with Payment
```typescript
case "create_resource": {
const { name, description } = args as {
name: string;
description: string;
};
// POST request with automatic payment
const response = await client.post("/api/resources", {
name,
description
});
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
resource: response.data,
payment_processed: true
}, null, 2)
}]
};
}
```
### Pattern 3: Demo Mode Fallback
```typescript
case "search_api": {
const { query } = args as { query: string };
// Check if payment mode is enabled
if (!paymentEnabled) {
// Return sample data with setup instructions
return {
content: [{
type: "text",
text: JSON.stringify({
demo_mode: true,
message: "X402 payment not configured - returning sample data",
sample_data: {
query,
results: ["Sample Result 1", "Sample Result 2"]
},
setup_instructions: {
step_1: "Get testnet USDC from https://faucet.circle.com/",
step_2: "Add PRIVATE_KEY to .env",
step_3: "Rebuild and try again"
}
}, null, 2)
}]
};
}
// Real API call with payment
const response = await client.post("/api/search", { query });
return {
content: [{
type: "text",
text: JSON.stringify(response.data, null, 2)
}]
};
}
```
### Pattern 4: Error Handling with Payment Context
```typescript
case "paid_endpoint": {
try {
const response = await client.get("/api/paid-endpoint");
return {
content: [{
type: "text",
text: JSON.stringify(response.data, null, 2)
}]
};
} catch (error: any) {
// Handle 402 payment errors specifically
if (error.response?.status === 402) {
throw new McpError(
ErrorCode.InternalError,
`Payment Required: ${error.response.data?.error || "Insufficient USDC balance"}`
);
}
// Handle other errors
if (error.response?.status === 404) {
throw new McpError(
ErrorCode.InvalidParams,
"Resource not found"
);
}
throw new McpError(
ErrorCode.InternalError,
`API call failed: ${error.message}`
);
}
}
```
---
## Tool Development Guide
### Step-by-Step: Creating a New Tool
**1. Define Tool Schema**
```typescript
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_weather",
description: "Get current weather for a location using X402-protected weather API",
inputSchema: {
type: "object",
properties: {
location: {
type: "string",
description: "City name or coordinates (e.g., 'San Francisco' or '37.7749,-122.4194')"
},
units: {
type: "string",
enum: ["metric", "imperial"],
description: "Temperature units",
default: "metric"
}
},
required: ["location"]
}
}
]
};
});
```
**2. Implement Tool Handler**
```typescript
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "get_weather": {
const { location, units = "metric" } = args as {
location: string;
units?: string;
};
// Validate input
if (!location?.trim()) {
throw new McpError(
ErrorCode.InvalidParams,
"Location parameter is required"
);
}
// Demo mode
if (!paymentEnabled) {
return {
content: [{
type: "text",
text: JSON.stringify({
demo_mode: true,
sample_data: {
location,
temperature: 72,
conditions: "Sunny",
units
},
note: "Configure PRIVATE_KEY for real data"
}, null, 2)
}]
};
}
// Payment-enabled API call
try {
const response = await client.post("/api/weather", {
location: location.trim(),
units
});
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
payment_processed: true,
data: response.data,
metadata: {
cost: "$0.001",
network: process.env.NETWORK
}
}, null, 2)
}]
};
} catch (error: any) {
if (error.response?.status === 402) {
throw new McpError(
ErrorCode.InternalError,
"Payment Required: Insufficient USDC balance or payment failed"
);
}
throw error;
}
}
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
}
});
```
**3. Test Tool**
```bash
# Build
npm run build
# Test with inspector
npm run inspector
# Select tool: get_weather
# Enter arguments: { "location": "San Francisco" }
# Verify response
```
### Best Practices
**Schema Design**:
- ✅ Use descriptive field names and descriptions
- ✅ Set appropriate types and constraints
- ✅ Mark required fields explicitly
- ✅ Provide examples in descriptions
- ✅ Use enums for fixed options
- ❌ Don't use ambiguous parameter names
- ❌ Don't forget input validation
**Error Messages**:
- ✅ Provide actionable error messages
- ✅ Include context (what failed, why)
- ✅ Suggest solutions when possible
- ✅ Differentiate payment vs API errors
- ❌ Don't expose sensitive data in errors
- ❌ Don't use generic "error occurred" messages
**Response Format**:
- ✅ Return structured JSON
- ✅ Include metadata (cost, network, timestamp)
- ✅ Use consistent response shapes
- ✅ Pretty-print with `JSON.stringify(data, null, 2)`
- ❌ Don't return raw strings unless appropriate
- ❌ Don't mix response formats across tools
---
## Error Handling
### Common Error Scenarios
**1. Payment Required (402)**
```typescript
// Automatic retry by x402-axios
try {
const response = await client.get("/api/endpoint");
} catch (error: any) {
if (error.response?.status === 402) {
// This means retry with payment FAILED
// Possible causes:
// - Insufficient USDC balance
// - Invalid signature
// - Payment timeout
// - Network issues
throw new McpError(
ErrorCode.InternalError,
`Payment failed: ${error.response.data?.error || "Check USDC balance"}`
);
}
}
```
**2. Insufficient USDC Balance**
```typescript
// Detected during payment attempt
if (error.message?.includes("insufficient balance")) {
throw new McpError(
ErrorCode.InternalError,
"Insufficient USDC balance. Get testnet USDC from https://faucet.circle.com/"
);
}
```
**3. Invalid Private Key**
```typescript
// Caught during client initialization
try {
const account = privateKeyToAccount(privateKey);
client = withPaymentInterceptor(axios.create({ baseURL }), account);
} catch (error) {
console.error("❌ Invalid private key:", error);
// Fall back to demo mode
client = axios.create({ baseURL });
paymentEnabled = false;
}
```
**4. API Server Down**
```typescript
try {
const response = await client.get("/api/endpoint");
} catch (error: any) {
if (error.code === "ECONNREFUSED") {
throw new McpError(
ErrorCode.InternalError,
`API server unreachable at ${baseURL}`
);
}
}
```
**5. Payment Timeout**
```typescript
// X402 payments expire after maxTimeoutSeconds
if (error.message?.includes("timeout") || error.message?.includes("expired")) {
throw new McpError(
ErrorCode.InternalError,
"Payment timeout - signature expired. Please retry."
);
}
```
### Error Recovery Strategies
```typescript
// Retry with exponential backoff
async function callWithRetry(fn: () => Promise<any>, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error: any) {
if (error.response?.status === 402) {
// Don't retry payment failures
throw error;
}
if (i === maxRetries - 1) {
throw error;
}
// Wait with exponential backoff
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
}
// Usage
const response = await callWithRetry(() =>
client.post("/api/endpoint", data)
);
```
---
## Demo vs Payment Mode
### Demo Mode Configuration
**When to use**: Development, testing MCP integration, learning
```env
# .env configuration
PRIVATE_KEY=<your-private-key-here> # ← Placeholder or empty
RESOURCE_SERVER_URL=https://api.example.com
NETWORK=base-sepolia
```
**Behavior**:
```typescript
// Initialization
if (!privateKey || privateKey.includes("<")) {
client = axios.create({ baseURL });
paymentEnabled = false;
console.error("⚠️ Running in DEMO MODE");
}
// Tool responses
{
"demo_mode": true,
"message": "X402 payment not configured",
"sample_data": { /* ... */ },
"setup_instructions": {
"step_1": "Get testnet USDC",
"step_2": "Add PRIVATE_KEY to .env",
"step_3": "Rebuild: npm run build"
}
}
```
**Advantages**:
- ✅ No wallet or USDC needed
- ✅ Immediate testing
- ✅ Learn MCP integration
- ✅ Safe for experiments
**Limitations**:
- ❌ No real API calls
- ❌ Sample data only
- ❌ Can't test payment flow
### Payment Mode Configuration
**When to use**: Production, real data consumption
```env
# .env configuration
PRIVATE_KEY=0x1234567890abcdef... # ← Valid private key
RESOURCE_SERVER_URL=https://api.example.com
NETWORK=base-sepolia # or 'base' for mainnet
```
**Behavior**:
```typescript
// Initialization
const account = privateKeyToAccount(privateKey);
client = withPaymentInterceptor(axios.create({ baseURL }), account);
paymentEnabled = true;
console.error("✅ X402 Payment client initialized");
console.error(` Wallet: ${account.address}`);
// Tool responses
{
"success": true,
"payment_processed": true,
"data": { /* real API data */ },
"metadata": {
"cost": "$0.001",
"network": "base-sepolia",
"protocol": "X402 v1.0"
}
}
```
**Requirements**:
- ✅ Valid Ethereum private key
- ✅ USDC balance in wallet
- ✅ X402-enabled API server
**Cost**: Typically $0.001 - $0.01 per API request
---
## Service Discovery
### Fetching API Metadata
X402 APIs expose discovery metadata at `/.well-known/x402`:
```typescript
// Built-in service_info tool
case "service_info": {
try {
const response = await axios.get(`${baseURL}/.well-known/x402`);
return {
content: [{
type: "text",
text: JSON.stringify({
service_discovery: true,
...response.data,
connection_info: {
base_url: baseURL,
payment_enabled: paymentEnabled,
network: process.env.NETWORK
}
}, null, 2)
}]
};
} catch (error) {
// Service discovery not available
return {
content: [{
type: "text",
text: JSON.stringify({
service_discovery: false,
message: "API does not support service discovery",
connection_info: {
base_url: baseURL,
payment_enabled: paymentEnabled
}
}, null, 2)
}]
};
}
}
```
### Service Discovery Schema
```json
{
"service": "Places API",
"version": "1.0.0",
"description": "Google Places API with X402 micropayments",
"payment": {
"protocol": "x402 v1.0",
"price": "$0.001",
"network": "base-sepolia",
"gasless": true,
"facilitator": "https://x402.coinbase.com/facilitator"
},
"endpoints": {
"/api/places/text-search": {
"method": "POST",
"description": "Search for places by text query",
"tags": ["search", "places", "location"],
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query (e.g., 'coffee shops in San Francisco')"
},
"location": {
"type": "string",
"description": "Optional location bias (e.g., 'San Francisco')"
},
"radius": {
"type": "number",
"description": "Search radius in meters (max 50000)"
}
},
"required": ["query"]
},
"outputSchema": {
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"address": { "type": "string" },
"rating": { "type": "number" }
}
}
}
}
},
"examples": [
{
"input": {
"query": "coffee shops",
"location": "San Francisco",
"radius": 1000
},
"output": {
"results": [
{
"name": "Blue Bottle Coffee",
"address": "66 Mint St, San Francisco",
"rating": 4.5
}
]
}
}
]
}
}
}
```
### Using Discovery Data
**Dynamic Tool Generation**:
```typescript
// Generate MCP tools from service discovery
const discovery = await axios.get(`${baseURL}/.well-known/x402`);
const tools = Object.entries(discovery.data.endpoints).map(([path, config]: [string, any]) => ({
name: path.replace(/\//g, "_").replace(/-/g, "_"),
description: config.description,
inputSchema: config.inputSchema
}));
```
**AI-Friendly Descriptions**:
```typescript
// Service discovery provides rich descriptions for AI
{
"description": "Search for places using text queries. Returns place names, addresses, ratings, photos, and other details. Perfect for finding restaurants, shops, landmarks, or any type of business or location.",
"tags": ["search", "places", "location", "business", "poi"]
}
```
---
## Security Considerations
### Private Key Management
**DO**:
- ✅ Store private keys in environment variables
- ✅ Use separate wallets for testnet and mainnet
- ✅ Rotate keys regularly in production
- ✅ Use hardware wallets for high-value operations
- ✅ Encrypt `.env` files in production deployments
**DON'T**:
- ❌ Commit `.env` files to version control
- ❌ Share private keys via insecure channels
- ❌ Use production keys in development
- ❌ Embed keys directly in code
- ❌ Use same key across multiple services
### Payment Validation
```typescript
// Verify payment response headers
const paymentResponse = response.headers["x-payment-response"];
if (paymentResponse) {
// Decode and verify settlement proof
const proof = decodePaymentResponse(paymentResponse);
console.log("Payment settled:", proof);
}
```
### Rate Limiting
```typescript
// Implement client-side rate limiting
class RateLimiter {
private requests: number[] = [];
async checkLimit(maxPerMinute: number): Promise<void> {
const now = Date.now();
this.requests = this.requests.filter(t => now - t < 60000);
if (this.requests.length >= maxPerMinute) {
throw new McpError(
ErrorCode.InternalError,
"Rate limit exceeded. Please wait and try again."
);
}
this.requests.push(now);
}
}
const limiter = new RateLimiter();
// Before API call
await limiter.checkLimit(10); // Max 10 requests per minute
const response = await client.get("/api/endpoint");
```
### Input Validation
```typescript
// Validate all user inputs
function validateQuery(query: string): string {
if (!query || typeof query !== "string") {
throw new McpError(ErrorCode.InvalidParams, "Query must be a non-empty string");
}
if (query.length > 1000) {
throw new McpError(ErrorCode.InvalidParams, "Query too long (max 1000 characters)");
}
// Sanitize input
return query.trim().slice(0, 1000);
}
// Usage
const safeQuery = validateQuery(args.query);
```
---
## FAQ
### General Questions
**Q: Do I need a wallet to use this template?**
A: No! Demo mode works without a wallet. For real API calls, you need a wallet with USDC.
**Q: How much does each API call cost?**
A: Typically $0.001 - $0.01 per request. Check the API's `/.well-known/x402` endpoint for exact pricing.
**Q: Do I pay gas fees?**
A: No! The facilitator pays gas fees. You only pay the API price in USDC.
**Q: Can I use this on mainnet?**
A: Yes! Set `NETWORK=base` and use mainnet USDC. The MCP client code is identical for both networks.
### Payment Questions
**Q: What happens if I don't have enough USDC?**
A: The API call fails with a 402 error. The tool returns an error message asking you to add USDC.
**Q: How do I get testnet USDC?**
A: Visit https://faucet.circle.com/ and enter your wallet address.
**Q: Can payments fail?**
A: Yes. Common causes: insufficient balance, signature timeout, network issues. The tool returns detailed error messages.
**Q: Are payments refundable?**
A: No. Each payment authorizes a USDC transfer. Once settled, it's final.
### Technical Questions
**Q: What's the difference between x402-axios and x402-express?**
A:
- **x402-axios**: Client library for MCP servers to consume X402 APIs
- **x402-express**: Server middleware for creating X402-protected APIs
**Q: Can I use this template with other MCP clients?**
A: Yes! Any MCP client that supports the standard protocol can use these tools.
**Q: How do I add custom tools?**
A: See [Tool Development Guide](#tool-development-guide) above.
**Q: Can I use multiple API servers?**
A: Yes! Create multiple payment clients with different baseURLs and private keys.
**Q: How do I handle API rate limits?**
A: Implement client-side rate limiting (see [Security Considerations](#security-considerations)).
### Debugging Questions
**Q: Why am I getting "Running in DEMO MODE"?**
A: Your `PRIVATE_KEY` is not set or invalid. Check `.env` file.
**Q: Why is my payment failing?**
A: Check:
1. USDC balance (use explorer)
2. Network setting (base-sepolia vs base)
3. Private key validity
4. API server availability
**Q: How do I check my USDC balance?**
A:
- Testnet: https://sepolia.basescan.org/address/YOUR_ADDRESS
- Mainnet: https://basescan.org/address/YOUR_ADDRESS
**Q: Why isn't my tool appearing in Claude?**
A: Check:
1. Built successfully: `npm run build`
2. Claude Desktop config has correct path
3. Restarted Claude Desktop
4. Tool listed in `ListToolsRequestSchema` handler
---
## Additional Resources
- **X402 Protocol**: https://docs.cdp.coinbase.com/x402/docs/welcome
- **MCP Protocol**: https://modelcontextprotocol.io/
- **EIP-712**: https://eips.ethereum.org/EIPS/eip-712
- **EIP-3009**: https://eips.ethereum.org/EIPS/eip-3009
- **Base Network**: https://base.org/
- **Circle USDC**: https://www.circle.com/en/usdc
---
**Built with ❤️ for AI agents consuming X402 APIs**