Discord Raw API MCP Server
by hanweg
- build
- services
// src/services/coingecko.ts
export class CoinGeckoService {
apiKey;
coins = [];
lastUpdated = null;
baseUrl = "https://pro-api.coingecko.com/api/v3";
constructor(apiKey) {
this.apiKey = apiKey;
}
// Core data fetching methods
async refreshCoinList() {
try {
const response = await fetch(`${this.baseUrl}/coins/list?include_platform=true`, {
headers: {
"X-Cg-Pro-Api-Key": this.apiKey,
},
});
if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
}
this.coins = await response.json();
this.lastUpdated = new Date();
}
catch (error) {
console.error("Error refreshing coin cache:", error);
throw error;
}
}
validateDateRange(fromDate, toDate) {
const now = new Date();
const from = new Date(fromDate);
const to = new Date(toDate);
if (from > now || to > now) {
throw new Error("Cannot request future dates");
}
if (from > to) {
throw new Error("Start date must be before end date");
}
}
async getHistoricalDataByDate(id, vs_currency, fromDate, // Format: YYYY-MM-DD
toDate, // Format: YYYY-MM-DD
interval) {
this.validateDateRange(fromDate, toDate);
// Convert dates to timestamps
const from = Math.floor(new Date(fromDate).getTime() / 1000);
const to = Math.floor(new Date(toDate).getTime() / 1000);
let url = `${this.baseUrl}/coins/${id}/market_chart/range?vs_currency=${vs_currency}&from=${from}&to=${to}`;
if (interval) {
url += `&interval=${interval}`;
}
try {
const response = await fetch(url, {
headers: {
"X-Cg-Pro-Api-Key": this.apiKey,
},
});
if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
}
return await response.json();
}
catch (error) {
console.error("Error fetching historical data:", error);
throw error;
}
}
async getOHLCDataByDate(id, vs_currency, fromDate, // Format: YYYY-MM-DD
toDate, // Format: YYYY-MM-DD
interval) {
this.validateDateRange(fromDate, toDate);
// Convert dates to timestamps
const from = Math.floor(new Date(fromDate).getTime() / 1000);
const to = Math.floor(new Date(toDate).getTime() / 1000);
const url = `${this.baseUrl}/coins/${id}/ohlc/range?vs_currency=${vs_currency}&from=${from}&to=${to}&interval=${interval}`;
console.error(`Making request to: ${url}`);
try {
const response = await fetch(url, {
headers: {
"X-Cg-Pro-Api-Key": this.apiKey,
"accept": "application/json"
},
});
if (!response.ok) {
const responseText = await response.text();
console.error(`API Response Status: ${response.status} ${response.statusText}`);
console.error(`API Response Headers:`, Object.fromEntries(response.headers.entries()));
console.error(`API Response Body:`, responseText);
throw new Error(`API request failed: ${response.status} ${response.statusText} - ${responseText}`);
}
const data = await response.json();
// Transform the data into a more readable format
// CoinGecko returns [timestamp, open, high, low, close]
return data.map(([timestamp, open, high, low, close]) => ({
timestamp,
open,
high,
low,
close
}));
}
catch (error) {
console.error("Error fetching OHLC data:", error);
throw error;
}
}
// Convenience methods for common time ranges
async getLast7Days(id, vs_currency) {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 7);
return this.getHistoricalDataByDate(id, vs_currency, start.toISOString().split('T')[0], end.toISOString().split('T')[0], 'daily');
}
async getLast30Days(id, vs_currency) {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 30);
return this.getHistoricalDataByDate(id, vs_currency, start.toISOString().split('T')[0], end.toISOString().split('T')[0], 'daily');
}
// Utility methods for accessing cached data
getCoins(page = 1, pageSize = 100) {
const start = (page - 1) * pageSize;
const end = start + pageSize;
return this.coins.slice(start, end);
}
findCoinIds(coinNames) {
return coinNames.map((name) => {
const normalizedName = name.toLowerCase();
const coin = this.coins.find((c) => c.name.toLowerCase() === normalizedName ||
c.symbol.toLowerCase() === normalizedName);
return {
name,
id: coin?.id || null,
};
});
}
getTotalPages(pageSize = 100) {
return Math.ceil(this.coins.length / pageSize);
}
getLastUpdated() {
return this.lastUpdated;
}
// Function calling schema definitions for different LLM providers
static getOpenAIFunctionDefinitions() {
const currentDate = new Date().toISOString().split('T')[0]; // Today's date in YYYY-MM-DD format
return [
{
name: "get_coins",
description: `Get a paginated list of all supported coins on CoinGecko. Data up to ${new Date().toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}`,
parameters: {
type: "object",
properties: {
page: {
type: "number",
description: "Page number (starts from 1, default: 1)",
minimum: 1
},
pageSize: {
type: "number",
description: "Results per page (default: 100, max: 1000)",
minimum: 1,
maximum: 1000
},
},
},
},
{
name: "find_coin_ids",
description: `Find CoinGecko IDs for a list of coin names or symbols. Data up to ${new Date().toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}`,
parameters: {
type: "object",
properties: {
coins: {
type: "array",
items: {
type: "string",
},
description: "Array of coin names or symbols to search for (e.g., ['BTC', 'ethereum', 'DOT'])",
maxItems: 100
},
},
required: ["coins"],
},
},
{
name: "get_historical_data",
description: `Get historical price, market cap, and volume data for a specific coin. Data up to ${new Date().toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}`,
parameters: {
type: "object",
properties: {
id: {
type: "string",
description: "CoinGecko coin ID (use find_coin_ids to lookup)",
},
vs_currency: {
type: "string",
description: "Target currency (e.g., 'usd', 'eur')",
},
from_date: {
type: "string",
description: "Start date in YYYY-MM-DD format (e.g., '2024-01-01')",
pattern: "^\\d{4}-\\d{2}-\\d{2}$"
},
to_date: {
type: "string",
description: "End date in YYYY-MM-DD format (e.g., '2024-12-30')",
pattern: "^\\d{4}-\\d{2}-\\d{2}$"
},
interval: {
type: "string",
enum: ["5m", "hourly", "daily"],
description: "Data interval - affects maximum time range: 5m (up to 1 day), hourly (up to 90 days), daily (up to 365 days)",
},
},
required: ["id", "vs_currency", "from_date", "to_date"],
},
},
{
name: "refresh_cache",
description: "Manually update the local cache of CoinGecko coin data (automatically refreshed periodically, only needed if seeing stale data)",
parameters: {
type: "object",
properties: {},
},
},
{
name: "get_ohlc_data",
description: `Get OHLC (Open, High, Low, Close) candlestick data for a specific coin within a time range. Data up to ${new Date().toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}`,
parameters: {
type: "object",
properties: {
id: {
type: "string",
description: "CoinGecko coin ID (use find_coin_ids to lookup)",
},
vs_currency: {
type: "string",
description: "Target currency (e.g., 'usd', 'eur')",
},
from_date: {
type: "string",
description: "Start date in YYYY-MM-DD format (e.g., '2024-01-01')",
pattern: "^\\d{4}-\\d{2}-\\d{2}$"
},
to_date: {
type: "string",
description: "End date in YYYY-MM-DD format (e.g., '2024-12-30')",
pattern: "^\\d{4}-\\d{2}-\\d{2}$"
},
interval: {
type: "string",
enum: ["daily", "hourly"],
description: "Data interval - daily (up to 180 days) or hourly (up to 31 days)",
},
},
required: ["id", "vs_currency", "from_date", "to_date", "interval"],
},
},
];
}
}