Skip to main content
Glama

X402 MCP Template

by CodaLabs-xyz
index.ts12.3 kB
#!/usr/bin/env node /** * X402 MCP Template * * Generic MCP server template for consuming X402-protected APIs with gasless micropayments. * Supports both demo mode (no private key) and payment mode (with wallet). * * Features: * - Automatic X402 payment handling via x402-axios * - Demo mode with sample data when no private key provided * - Service discovery integration * - Error handling and payment flow management * - Claude Desktop compatible * * Setup: * 1. Copy .env.example to .env * 2. Configure RESOURCE_SERVER_URL (your X402 API endpoint) * 3. Optional: Add PRIVATE_KEY for payment mode * 4. Run: npm run build * 5. Test: npm run inspector */ import { config } from "dotenv"; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js"; import axios from "axios"; import { privateKeyToAccount } from "viem/accounts"; import { withPaymentInterceptor } from "x402-axios"; config(); // ============================================================================= // CONFIGURATION // ============================================================================= const privateKey = process.env.PRIVATE_KEY as `0x${string}`; const baseURL = process.env.RESOURCE_SERVER_URL || "https://your-x402-api.example.com"; const network = process.env.NETWORK || "base-sepolia"; // ============================================================================= // PAYMENT CLIENT INITIALIZATION // ============================================================================= let client: any; let paymentEnabled = false; if (privateKey && !privateKey.includes("<") && privateKey.startsWith("0x") && privateKey.length === 66) { try { const account = privateKeyToAccount(privateKey); client = withPaymentInterceptor(axios.create({ baseURL }), account); paymentEnabled = true; console.error("✅ X402 Payment client initialized"); console.error(` Wallet: ${account.address}`); console.error(` Network: ${network}`); console.error(` API: ${baseURL}`); } catch (error) { console.error("❌ Failed to initialize payment client:", error); console.error(" Falling back to demo mode"); client = axios.create({ baseURL }); } } else { client = axios.create({ baseURL }); console.error("⚠️ Running in DEMO MODE - no private key provided"); console.error(" Tools will return sample data with setup instructions"); console.error(` API: ${baseURL}`); } // ============================================================================= // MCP SERVER SETUP // ============================================================================= const server = new Server( { name: "x402-mcp-template", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // ============================================================================= // TOOL DEFINITIONS // ============================================================================= /** * TODO: Customize these tool definitions for your specific API * * Each tool should: * 1. Define clear name and description * 2. Specify inputSchema matching your API requirements * 3. Make payment-enabled API calls using the client * 4. Handle errors gracefully * 5. Provide demo mode fallback */ server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "example_api_call", description: "Example tool for making X402-protected API calls. Replace with your actual API endpoints.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Example query parameter - customize for your API", }, limit: { type: "number", description: "Optional: Maximum number of results to return", minimum: 1, maximum: 100, default: 10, }, }, required: ["query"], }, }, { name: "service_info", description: "Get information about the X402 API service including available endpoints, pricing, and payment requirements", inputSchema: { type: "object", properties: {}, }, }, { name: "health_check", description: "Check if the X402 API service is available and responding", inputSchema: { type: "object", properties: {}, }, }, ], }; }); // ============================================================================= // TOOL HANDLERS // ============================================================================= server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "example_api_call": { const { query, limit = 10 } = args as { query: string; limit?: number }; if (!query?.trim()) { throw new McpError(ErrorCode.InvalidParams, "Query parameter is required"); } // Demo mode - return sample data with setup instructions if (!paymentEnabled) { return { content: [ { type: "text", text: JSON.stringify( { demo_mode: true, message: "X402 payment client not configured - returning sample data", sample_data: { query: query, limit: limit, results: [ { id: "sample-1", name: "Sample Result 1", description: "This is sample data returned in demo mode", }, { id: "sample-2", name: "Sample Result 2", description: "Configure PRIVATE_KEY in .env to enable real payments", }, ], }, setup_instructions: { step_1: "Get testnet USDC from https://faucet.circle.com/", step_2: "Add your private key to .env file: PRIVATE_KEY=0x...", step_3: "Set RESOURCE_SERVER_URL to your X402 API endpoint", step_4: "Rebuild and restart: npm run build", step_5: "Try the tool again - payments will be automatic!", }, payment_info: { protocol: "X402 v1.0", network: network, cost: "Typically $0.001 - $0.01 per request", gasless: true, note: "You only pay for the API call, no gas fees!", }, }, null, 2 ), }, ], }; } // Payment mode - make real API call // TODO: Customize endpoint path and request structure for your API const endpointPath = "/api/your-endpoint"; // ← Replace with your actual endpoint try { const response = await client.post(endpointPath, { query: query.trim(), limit: limit, }); return { content: [ { type: "text", text: JSON.stringify( { success: true, payment_processed: true, data: response.data, metadata: { endpoint: endpointPath, payment_protocol: "X402 v1.0", network: network, }, }, null, 2 ), }, ], }; } catch (error: any) { // Handle X402 payment errors if (error.response?.status === 402) { throw new McpError( ErrorCode.InternalError, `Payment Required: ${error.response.data?.error || "Insufficient USDC balance or payment failed"}` ); } throw error; } } case "service_info": { // Fetch service discovery metadata from /.well-known/x402 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: network, }, }, null, 2 ), }, ], }; } catch (error: any) { // If service discovery not available, return basic info return { content: [ { type: "text", text: JSON.stringify( { service_discovery: false, message: "Service discovery endpoint not available", connection_info: { base_url: baseURL, payment_enabled: paymentEnabled, network: network, }, note: "API may not support X402 protocol or /.well-known/x402 endpoint", }, null, 2 ), }, ], }; } } case "health_check": { try { // Try health endpoint const response = await axios.get(`${baseURL}/health`, { timeout: 5000 }); return { content: [ { type: "text", text: JSON.stringify( { status: "healthy", api_url: baseURL, payment_enabled: paymentEnabled, network: network, response: response.data, }, null, 2 ), }, ], }; } catch (error: any) { return { content: [ { type: "text", text: JSON.stringify( { status: "error", api_url: baseURL, payment_enabled: paymentEnabled, network: network, error: error.message, note: "API may be down or health endpoint not available", }, null, 2 ), }, ], }; } } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error: any) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error.message}` ); } }); // ============================================================================= // SERVER STARTUP // ============================================================================= async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("🚀 X402 MCP Template server running"); console.error(" Status: Ready for connections"); console.error(" Payment Mode:", paymentEnabled ? "ENABLED ✅" : "DEMO MODE ⚠️"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

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