Discord Raw API MCP Server

This file is a merged representation of the entire codebase, combining all repository files into a single document. Generated by Repomix on: 2024-12-30T18:38:24.031Z ================================================================ File Summary ================================================================ Purpose: -------- This file contains a packed representation of the entire repository's contents. It is designed to be easily consumable by AI systems for analysis, code review, or other automated processes. File Format: ------------ The content is organized as follows: 1. This summary section 2. Repository information 3. Directory structure 4. Multiple file entries, each consisting of: a. A separator line (================) b. The file path (File: path/to/file) c. Another separator line d. The full contents of the file e. A blank line Usage Guidelines: ----------------- - This file should be treated as read-only. Any changes should be made to the original repository files, not this packed version. - When processing this file, use the file path to distinguish between different files in the repository. - Be aware that this file may contain sensitive information. Handle it with the same level of security as you would the original repository. Notes: ------ - Some files may have been excluded based on .gitignore rules and Repomix's configuration. - Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files. Additional Info: ---------------- For more information about Repomix, visit: https://github.com/yamadashy/repomix ================================================================ Directory Structure ================================================================ src/ services/ coingecko.ts index.ts server.ts .env.example .gitignore notes package.json tsconfig.json ================================================================ Files ================================================================ ================ File: src/services/coingecko.ts ================ // src/services/coingecko.ts export interface CoinInfo { id: string; symbol: string; name: string; platforms?: Record<string, string>; } export interface HistoricalData { prices: [number, number][]; market_caps: [number, number][]; total_volumes: [number, number][]; } export class CoinGeckoService { private coins: CoinInfo[] = []; private lastUpdated: Date | null = null; private readonly baseUrl = "https://pro-api.coingecko.com/api/v3"; constructor(private apiKey: string) {} // Core data fetching methods async refreshCoinList(): Promise<void> { 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; } } async getHistoricalData( id: string, vs_currency: string, from: number, to: number, interval?: "5m" | "hourly" | "daily" ): Promise<HistoricalData> { 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; } } // Utility methods for accessing cached data getCoins(page: number = 1, pageSize: number = 100): CoinInfo[] { const start = (page - 1) * pageSize; const end = start + pageSize; return this.coins.slice(start, end); } findCoinIds(coinNames: string[]): { name: string; id: string | null }[] { 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: number = 100): number { return Math.ceil(this.coins.length / pageSize); } getLastUpdated(): Date | null { return this.lastUpdated; } // Function calling schema definitions for different LLM providers static getOpenAIFunctionDefinitions() { return [ { name: "get_coins", description: "Get a paginated list of all supported coins on CoinGecko", parameters: { type: "object", properties: { page: { type: "number", description: "Page number (starts from 1)", }, pageSize: { type: "number", description: "Number of items per page (max 1000)", }, }, }, }, { name: "find_coin_ids", description: "Find CoinGecko IDs for a list of coin names or symbols", parameters: { type: "object", properties: { coins: { type: "array", items: { type: "string", }, description: "Array of coin names or symbols to look up", }, }, 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", }, vs_currency: { type: "string", description: "Target currency (e.g., 'usd', 'eur')", }, from: { type: "number", description: "Start timestamp (UNIX)", }, to: { type: "number", description: "End timestamp (UNIX)", }, interval: { type: "string", enum: ["5m", "hourly", "daily"], description: "Data interval (optional)", }, }, required: ["id", "vs_currency", "from", "to"], }, }, { name: "refresh_cache", description: "Refresh the cached list of coins from CoinGecko", parameters: { type: "object", properties: {}, }, }, ]; } } ================ File: src/index.ts ================ // src/index.ts import { CoinGeckoMCPServer } from "./server.js"; import { CoinGeckoService } from "./services/coingecko.js"; import * as dotenv from 'dotenv'; dotenv.config(); // Export for use as a library export { CoinGeckoService, CoinGeckoMCPServer }; // Run as standalone server if executed directly if (import.meta.url === `file://${process.argv[1]}`) { const apiKey = process.env.COINGECKO_API_KEY; if (!apiKey) { console.log("COINGECKO_API_KEY is not set in the environment variables"); process.exit(1); } const server = new CoinGeckoMCPServer(apiKey); server.start().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); }); } ================ File: src/server.ts ================ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; import { CoinGeckoService } from "./services/coingecko.js"; // Input validation schemas const GetCoinsArgumentsSchema = z.object({ page: z.number().min(1).optional(), pageSize: z.number().min(1).max(1000).optional(), }); const FindCoinIdsArgumentsSchema = z.object({ coins: z.array(z.string()), }); const GetHistoricalDataArgumentsSchema = z.object({ id: z.string(), vs_currency: z.string(), from: z.number(), to: z.number(), interval: z.enum(["5m", "hourly", "daily"]).optional(), }); export class CoinGeckoMCPServer { private server: Server; private coinGeckoService: CoinGeckoService; constructor(apiKey: string) { this.coinGeckoService = new CoinGeckoService(apiKey); // Initialize MCP server this.server = new Server( { name: "coingecko", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // Set up request handlers this.setupRequestHandlers(); // Initialize cache this.coinGeckoService.refreshCoinList().catch((error) => { console.error("Failed to initialize coin cache:", error); process.exit(1); }); } private setupRequestHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "get-coins", description: "Get a paginated list of all supported coins on CoinGecko", inputSchema: { type: "object", properties: { page: { type: "number", description: "Page number (starts from 1)", }, pageSize: { type: "number", description: "Number of items per page (max 1000)", }, }, }, }, { name: "find-coin-ids", description: "Find CoinGecko IDs for a list of coin names or symbols", inputSchema: { type: "object", properties: { coins: { type: "array", items: { type: "string", }, description: "Array of coin names or symbols to look up", }, }, 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' })}`, inputSchema: { type: "object", properties: { id: { type: "string", description: "CoinGecko coin ID", }, vs_currency: { type: "string", description: "Target currency (e.g., 'usd', 'eur')", }, from: { type: "number", description: "Start timestamp (UNIX)", }, to: { type: "number", description: "End timestamp (UNIX)", }, interval: { type: "string", enum: ["5m", "hourly", "daily"], description: "Data interval (optional)", }, }, required: ["id", "vs_currency", "from", "to"], }, }, { name: "refresh-cache", description: "Refresh the cached list of coins from CoinGecko", inputSchema: { type: "object", properties: {}, }, }, ], }; }); // Handle tool execution this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { if (name === "get-coins") { const { page = 1, pageSize = 100 } = GetCoinsArgumentsSchema.parse(args); const coins = this.coinGeckoService.getCoins(page, pageSize); const totalPages = this.coinGeckoService.getTotalPages(pageSize); const lastUpdated = this.coinGeckoService.getLastUpdated(); return { content: [ { type: "text", text: JSON.stringify( { coins, pagination: { currentPage: page, totalPages, pageSize, }, lastUpdated: lastUpdated?.toISOString(), }, null, 2 ), }, ], }; } if (name === "find-coin-ids") { const { coins } = FindCoinIdsArgumentsSchema.parse(args); const results = this.coinGeckoService.findCoinIds(coins); return { content: [ { type: "text", text: JSON.stringify(results, null, 2), }, ], }; } if (name === "get-historical-data") { const { id, vs_currency, from, to, interval } = GetHistoricalDataArgumentsSchema.parse(args); const data = await this.coinGeckoService.getHistoricalData( id, vs_currency, from, to, interval ); return { content: [ { type: "text", text: JSON.stringify( { timeRange: { from: new Date(from * 1000).toISOString(), to: new Date(to * 1000).toISOString(), }, interval: interval || "auto", data, }, null, 2 ), }, ], }; } if (name === "refresh-cache") { await this.coinGeckoService.refreshCoinList(); const lastUpdated = this.coinGeckoService.getLastUpdated(); return { content: [ { type: "text", text: `Cache refreshed successfully at ${lastUpdated?.toISOString()}`, }, ], }; } throw new Error(`Unknown tool: ${name}`); } catch (error) { if (error instanceof z.ZodError) { throw new Error( `Invalid arguments: ${error.errors .map((e) => `${e.path.join(".")}: ${e.message}`) .join(", ")}` ); } throw error; } }); } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("CoinGecko MCP Server running on stdio"); } } ================ File: .env.example ================ COINGECKO_API_KEY=your_api_key_here ================ File: .gitignore ================ # Created by https://www.toptal.com/developers/gitignore/api/node # Edit at https://www.toptal.com/developers/gitignore?templates=node ### Node ### # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ### Node Patch ### # Serverless Webpack directories .webpack/ # Optional stylelint cache # SvelteKit build / generate output .svelte-kit # End of https://www.toptal.com/developers/gitignore/api/node ================ File: notes ================ 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", } )}`, ================ File: package.json ================ { "name": "coingecko-server", "version": "1.0.0", "main": "index.js", "type": "module", "scripts": { "build": "tsc" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "@modelcontextprotocol/sdk": "^1.0.4", "dotenv": "^16.4.7", "zod": "^3.24.1" }, "devDependencies": { "@types/node": "^22.10.2", "typescript": "^5.7.2" } } ================ File: tsconfig.json ================ { "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./build", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"], "exclude": ["node_modules"] }