get_crypto_prices
Fetch live BTC, ETH, and SOL prices along with top 24-hour gainers and losers. Data sourced from CoinGecko, with a micropayment of 0.001 USDC per call.
Instructions
Get live cryptocurrency prices and top 24h movers. Returns BTC, ETH, SOL prices plus top gainers/losers. Costs 0.001 USDC per call (x402 micropayment on Base). Data sourced live from CoinGecko.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/index.ts:246-258 (registration)Tool registered in the TOOLS array with name 'get_crypto_prices', description, and an empty inputSchema (no parameters required).
const TOOLS = [ { name: 'get_crypto_prices', description: 'Get live cryptocurrency prices and top 24h movers. Returns BTC, ETH, SOL prices plus top gainers/losers. ' + 'Costs 0.001 USDC per call (x402 micropayment on Base). ' + 'Data sourced live from CoinGecko.', inputSchema: { type: 'object', properties: {}, required: [], }, }, - src/index.ts:446-448 (handler)Handler case in CallToolRequestSchema: calls callApi('/api/price-feed') which fetches live crypto prices via the x402 payment API.
case 'get_crypto_prices': result = await callApi('/api/price-feed'); break; - src/index.ts:114-180 (helper)The callApi helper function handles all API requests, including x402 payment negotiation (auto-pay via wallet or manual 402 response) and 30-second timeout.
async function callApi( endpoint: string, params: Record<string, string | number | undefined> = {} ): Promise<ApiResponse> { const url = new URL(`${API_BASE_URL}${endpoint}`); for (const [key, value] of Object.entries(params)) { if (value !== undefined && value !== '') { url.searchParams.set(key, String(value)); } } const fetchFn = await getX402Fetch(); let response: Response; const controller = new AbortController(); const fetchTimeout = setTimeout(() => controller.abort(), 30_000); try { response = await fetchFn(url.toString(), { headers: { 'Accept': 'application/json', 'User-Agent': `x402-api-mcp/${SERVER_VERSION}`, }, signal: controller.signal, }); } catch (err) { const isTimeout = err instanceof Error && err.name === 'AbortError'; throw new McpError( ErrorCode.InternalError, isTimeout ? `Request to ${endpoint} timed out after 30 seconds` : `Network error calling ${endpoint}: ${err instanceof Error ? err.message : String(err)}` ); } finally { clearTimeout(fetchTimeout); } if (response.status === 402) { // Clone before reading so we can fall back to text() if JSON parsing fails. // Without the clone, calling response.json() consumes the body; a subsequent // response.text() call then throws "body already used". const cloned = response.clone(); let paymentDetails: unknown; try { paymentDetails = await response.json(); } catch { paymentDetails = await cloned.text(); } return { status: 402, data: null, paymentRequired: true, paymentDetails }; } if (!response.ok) { const errorText = await response.text(); if (response.status === 400 || response.status === 422) { throw new McpError( ErrorCode.InvalidParams, `Invalid request to ${endpoint}: ${errorText}` ); } throw new McpError( ErrorCode.InternalError, `API error ${response.status} from ${endpoint}: ${errorText}` ); } const data = await response.json(); return { status: response.status, data }; } - src/index.ts:107-108 (helper)The ApiResponse interface used by the handler to structure the return value.
interface ApiResponse { status: number;