Skip to main content
Glama

X402 MCP Template

by CodaLabs-xyz
CLAUDE.md33.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**

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/CodaLabs-xyz/Template-x402-Mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server