Skip to main content
Glama
benschiller

CryptoTwitter.Space x402 MCP Server

by benschiller
x402ExpressServer.tsβ€’6.67 kB
import { config } from "dotenv"; import express, { Request, Response, Application } from "express"; import { paymentMiddleware } from "x402-express"; import fetch from "node-fetch"; import { distributeIncomingPayment, validateAddresses } from "./smartDistribution.js"; // Load environment variables from .env file config(); const REPORTS_API_BASE = "https://cryptotwitter.space"; // Validate and get the receiving wallet address from environment variables const RECEIVING_WALLET_ADDRESS = process.env.RECEIVING_WALLET_ADDRESS as `0x${string}`; if (!RECEIVING_WALLET_ADDRESS) { throw new Error("RECEIVING_WALLET_ADDRESS is not set in .env file"); } // Validate distribution addresses on startup console.log("πŸ” Validating distribution addresses..."); if (!validateAddresses()) { console.error("❌ Address validation failed. Please check your .env file."); process.exit(1); } const app: Application = express(); app.use(express.json()); // Add debug logging middleware BEFORE payment middleware app.use((req, res, next) => { console.log(`\n=== INCOMING REQUEST ===`); console.log(`${req.method} ${req.path}`); console.log(`Full URL: ${req.url}`); console.log('Headers:', JSON.stringify(req.headers, null, 2)); console.log('User-Agent:', req.headers['user-agent']); console.log('========================\n'); next(); }); // Add the global payment middleware with dynamic route pattern app.use(paymentMiddleware( RECEIVING_WALLET_ADDRESS, { // Use a pattern that matches your dynamic route "GET /get-report-resource/*": { // Wildcard to match any report ID price: "$0.01", network: "base-sepolia", }, }, { url: "https://www.x402.org/facilitator", } )); // Helper function for making HTTP requests async function makeHttpRequest<T>(url: string): Promise<T | null> { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return (await response.json()) as T; } else { return (await response.text()) as T; } } catch (error) { console.error("Error making HTTP request:", error); return null; } } interface Report { id: string; status: string; curator_user_id: string; space_title: string; } interface ReportsApiResponse { data: Report[]; hasMore: boolean; } // Route for search-reports (remains free) app.get("/search-reports", async (req: Request, res: Response) => { const query = req.query.query as string; if (!query) { res.status(400).json({ error: "Query parameter is required." }); return; } const reportsUrl = `${REPORTS_API_BASE}/api/reports?page=1`; const reportsData = await makeHttpRequest<ReportsApiResponse>(reportsUrl); if (!reportsData) { res.status(500).json({ error: "Failed to retrieve reports data." }); return; } const filteredReports = reportsData.data.filter(report => report.space_title.toLowerCase().includes(query.toLowerCase()) ); if (filteredReports.length === 0) { res.status(200).json({ message: `No reports found matching "${query}".` }); return; } const reportSummaries = filteredReports.map(report => ({ id: report.id, title: report.space_title, })); res.json(reportSummaries); }); // Route for get-report-resource (paid) - now with CDP Wallet API v2 distribution app.get( "/get-report-resource/:reportId", async (req: Request, res: Response) => { console.log("πŸ’° Payment validated! Processing paid request..."); const reportId = req.params.reportId; try { // First, get the report content console.log(`πŸ“„ Fetching report content for ID: ${reportId}`); const reportContentUrl = `${REPORTS_API_BASE}/api/report-resource/${reportId}`; const reportContent = await makeHttpRequest<string>(reportContentUrl); if (reportContent === null) { res.status(500).json({ error: `Failed to retrieve content for report ID: ${reportId}.` }); return; } console.log(`βœ… Content retrieved successfully for report ID: ${reportId}`); // πŸš€ TRIGGER AUTOMATED PAYMENT DISTRIBUTION VIA CDP Wallet API v2 console.log("πŸ”„ Initiating automated payment distribution..."); // Fire-and-forget distribution (don't wait for completion) distributeIncomingPayment({ amountPaid: 0.01, // currency: "USDC", // network: "base-sepolia" }).then(() => { console.log("πŸŽ‰ Payment distribution completed successfully!"); }).catch(error => { console.error("❌ Payment distribution failed:", error); // In production, you might want to: // 1. Queue for retry // 2. Send alert to monitoring system // 3. Log to database for reconciliation }); // Immediately serve content to user (don't wait for distribution) console.log("πŸ“€ Serving content to user while distribution processes..."); res.send(reportContent); } catch (error) { console.error("❌ Error in get-report-resource:", error); res.status(500).json({ error: "Internal server error" }); } } ); // Health check endpoint with distribution config info app.get("/health", (req: Request, res: Response) => { res.json({ status: "healthy", timestamp: new Date().toISOString(), receivingWallet: RECEIVING_WALLET_ADDRESS, distributionEnabled: true, usdcContract: process.env.USDC_CONTRACT_ADDRESS }); }); // New endpoint to check distribution configuration app.get("/distribution-config", (req: Request, res: Response) => { res.json({ hostAddress: process.env.HOST_ADDRESS, curatorAddress: process.env.CURATOR_ADDRESS, platformAddress: process.env.PLATFORM_ADDRESS, splits: { host: "50%", curator: "30%", platform: "20%" }, contracts: { usdc: process.env.USDC_CONTRACT_ADDRESS } }); }); const PORT = 4021; app.listen(PORT, () => { console.log(`\nπŸš€ x402 Express Server with CDP Wallet API v2 Integration`); console.log(`πŸ“‘ Listening at: http://localhost:${PORT}`); console.log(`πŸ’³ Receiving payments at: ${RECEIVING_WALLET_ADDRESS}`); console.log(`πŸͺ™ USDC contract: ${process.env.USDC_CONTRACT_ADDRESS}`); console.log(`πŸ”„ Automated 3-way distribution: ENABLED`); console.log(` πŸ“ Host (50%): ${process.env.HOST_ADDRESS}`); console.log(` πŸ“ Curator (30%): ${process.env.CURATOR_ADDRESS}`); console.log(` πŸ“ Platform (20%): ${process.env.PLATFORM_ADDRESS}\n`); });

Latest Blog Posts

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/benschiller/cts-x402-mcp'

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