Skip to main content
Glama
Bichev
by Bichev
index.js15.1 kB
import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import compression from 'compression'; import rateLimit from 'express-rate-limit'; import swaggerJsdoc from 'swagger-jsdoc'; import swaggerUi from 'swagger-ui-express'; import { config } from 'dotenv'; import { createLogger, format, transports } from 'winston'; import { CoinbaseClient } from '../../mcp-server/src/coinbase-client.js'; // Load environment variables config(); // Logger setup const logger = createLogger({ level: process.env.LOG_LEVEL || 'info', format: format.combine(format.timestamp(), format.errors({ stack: true }), format.json()), transports: [ new transports.File({ filename: 'logs/error.log', level: 'error' }), new transports.File({ filename: 'logs/combined.log' }), new transports.Console({ format: format.combine(format.colorize(), format.simple()) }) ] }); // Express app setup const app = express(); const PORT = process.env.PORT || 3002; // Middleware app.use(helmet()); app.use(cors()); app.use(compression()); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: { error: 'Too many requests from this IP, please try again later.', retryAfter: '15 minutes' }, standardHeaders: true, legacyHeaders: false }); app.use('/api/', limiter); // Coinbase client const coinbaseClient = new CoinbaseClient(process.env.COINBASE_API_URL); // Swagger documentation setup const swaggerOptions = { definition: { openapi: '3.0.0', info: { title: 'Coinbase Chat MCP API', version: '1.0.0', description: 'REST API for Coinbase public cryptocurrency data', license: { name: 'MIT', url: 'https://opensource.org/licenses/MIT' } }, servers: [ { url: `http://localhost:${PORT}`, description: 'Development server' } ] }, apis: ['./src/routes/*.ts', './src/index.ts'] }; const swaggerSpec = swaggerJsdoc(swaggerOptions); app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); // Health check endpoint /** * @swagger * /health: * get: * summary: Health check endpoint * description: Returns the health status of the API server * responses: * 200: * description: API is healthy * content: * application/json: * schema: * type: object * properties: * status: * type: string * example: "healthy" * timestamp: * type: string * format: date-time * uptime: * type: number * description: Server uptime in seconds */ app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime() }); }); // API Routes /** * @swagger * /api/v1/prices/{currencyPair}: * get: * summary: Get current spot price * description: Get the current spot price for a cryptocurrency pair * parameters: * - in: path * name: currencyPair * required: true * schema: * type: string * description: Currency pair (e.g., BTC-USD) * example: BTC-USD * responses: * 200: * description: Current spot price * content: * application/json: * schema: * type: object * properties: * data: * type: object * properties: * amount: * type: string * example: "45000.00" * base: * type: string * example: "BTC" * currency: * type: string * example: "USD" * 400: * description: Invalid currency pair * 500: * description: Internal server error */ app.get('/api/v1/prices/:currencyPair', async (req, res) => { try { const { currencyPair } = req.params; const data = await coinbaseClient.getSpotPrice(currencyPair); res.json(data); } catch (error) { logger.error('Error fetching spot price:', error); res.status(500).json({ error: 'Failed to fetch spot price', message: error instanceof Error ? error.message : 'Unknown error' }); } }); /** * @swagger * /api/v1/prices/{currencyPair}/historical: * get: * summary: Get historical prices * description: Get historical price data for a cryptocurrency pair * parameters: * - in: path * name: currencyPair * required: true * schema: * type: string * description: Currency pair (e.g., BTC-USD) * - in: query * name: start * schema: * type: string * format: date * description: Start date (YYYY-MM-DD) * - in: query * name: end * schema: * type: string * format: date * description: End date (YYYY-MM-DD) * - in: query * name: period * schema: * type: string * enum: [hour, day] * default: day * description: Data granularity * responses: * 200: * description: Historical price data * 400: * description: Invalid parameters * 500: * description: Internal server error */ app.get('/api/v1/prices/:currencyPair/historical', async (req, res) => { try { const { currencyPair } = req.params; const { start, end, period = 'day' } = req.query; const data = await coinbaseClient.getHistoricalPrices(currencyPair, start, end, period); res.json(data); } catch (error) { logger.error('Error fetching historical prices:', error); res.status(500).json({ error: 'Failed to fetch historical prices', message: error instanceof Error ? error.message : 'Unknown error' }); } }); /** * @swagger * /api/v1/exchange-rates: * get: * summary: Get exchange rates * description: Get exchange rates for a base currency * parameters: * - in: query * name: currency * required: true * schema: * type: string * description: Base currency code * example: USD * responses: * 200: * description: Exchange rates data * 400: * description: Missing currency parameter * 500: * description: Internal server error */ app.get('/api/v1/exchange-rates', async (req, res) => { try { const { currency } = req.query; if (!currency) { return res.status(400).json({ error: 'Currency parameter is required' }); } const data = await coinbaseClient.getExchangeRates(currency); res.json(data); } catch (error) { logger.error('Error fetching exchange rates:', error); res.status(500).json({ error: 'Failed to fetch exchange rates', message: error instanceof Error ? error.message : 'Unknown error' }); } }); /** * @swagger * /api/v1/assets: * get: * summary: Get all available assets * description: Get list of all available cryptocurrencies and fiat currencies * parameters: * - in: query * name: search * schema: * type: string * description: Search query for asset filtering * - in: query * name: limit * schema: * type: integer * default: 50 * description: Maximum number of results * responses: * 200: * description: List of assets * 500: * description: Internal server error */ app.get('/api/v1/assets', async (req, res) => { try { const { search, limit = '50' } = req.query; if (search) { const data = await coinbaseClient.searchAssets(search, parseInt(limit)); res.json({ data }); } else { const data = await coinbaseClient.getCurrencies(); const limitedData = { ...data, data: data.data.slice(0, parseInt(limit)) }; res.json(limitedData); } } catch (error) { logger.error('Error fetching assets:', error); res.status(500).json({ error: 'Failed to fetch assets', message: error instanceof Error ? error.message : 'Unknown error' }); } }); /** * @swagger * /api/v1/assets/{assetId}: * get: * summary: Get asset details * description: Get detailed information about a specific asset * parameters: * - in: path * name: assetId * required: true * schema: * type: string * description: Asset ID or symbol * example: BTC * responses: * 200: * description: Asset details * 404: * description: Asset not found * 500: * description: Internal server error */ app.get('/api/v1/assets/:assetId', async (req, res) => { try { const { assetId } = req.params; const data = await coinbaseClient.getAssetDetails(assetId); if (!data) { return res.status(404).json({ error: 'Asset not found' }); } res.json({ data }); } catch (error) { logger.error('Error fetching asset details:', error); res.status(500).json({ error: 'Failed to fetch asset details', message: error instanceof Error ? error.message : 'Unknown error' }); } }); /** * @swagger * /api/v1/markets/{currencyPair}/stats: * get: * summary: Get market statistics * description: Get 24-hour market statistics for a currency pair * parameters: * - in: path * name: currencyPair * required: true * schema: * type: string * description: Currency pair * example: BTC-USD * responses: * 200: * description: Market statistics * 500: * description: Internal server error */ app.get('/api/v1/markets/:currencyPair/stats', async (req, res) => { try { const { currencyPair } = req.params; const data = await coinbaseClient.getStats(currencyPair); res.json(data); } catch (error) { logger.error('Error fetching market stats:', error); res.status(500).json({ error: 'Failed to fetch market stats', message: error instanceof Error ? error.message : 'Unknown error' }); } }); /** * @swagger * /api/v1/popular-pairs: * get: * summary: Get popular trading pairs * description: Get a list of popular cryptocurrency trading pairs * responses: * 200: * description: List of popular trading pairs * content: * application/json: * schema: * type: object * properties: * data: * type: array * items: * type: string * example: "BTC-USD" */ app.get('/api/v1/popular-pairs', async (req, res) => { try { const data = await coinbaseClient.getPopularPairs(); res.json({ data }); } catch (error) { logger.error('Error fetching popular pairs:', error); res.status(500).json({ error: 'Failed to fetch popular pairs', message: error instanceof Error ? error.message : 'Unknown error' }); } }); /** * @swagger * /api/v1/analysis/{currencyPair}: * get: * summary: Analyze price data * description: Perform technical analysis on cryptocurrency price data * parameters: * - in: path * name: currencyPair * required: true * schema: * type: string * description: Currency pair * example: BTC-USD * - in: query * name: period * schema: * type: string * enum: [1d, 7d, 30d, 1y] * default: 1d * description: Analysis period * - in: query * name: metrics * schema: * type: array * items: * type: string * enum: [volatility, trend, support_resistance, volume] * description: Analysis metrics to include * style: form * explode: false * responses: * 200: * description: Price analysis results * 500: * description: Internal server error */ app.get('/api/v1/analysis/:currencyPair', async (req, res) => { try { const { currencyPair } = req.params; const { period = '1d', metrics } = req.query; const metricsArray = metrics ? (Array.isArray(metrics) ? metrics : [metrics]) : ['volatility', 'trend']; const data = await coinbaseClient.analyzePriceData(currencyPair, period, metricsArray); res.json({ data }); } catch (error) { logger.error('Error analyzing price data:', error); res.status(500).json({ error: 'Failed to analyze price data', message: error instanceof Error ? error.message : 'Unknown error' }); } }); // Error handling middleware app.use((err, req, res, next) => { logger.error('Unhandled error:', err); res.status(500).json({ error: 'Internal server error', message: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong' }); }); // 404 handler app.use('*', (req, res) => { res.status(404).json({ error: 'Route not found', path: req.originalUrl }); }); // Start server const server = app.listen(PORT, () => { logger.info(`API Server running on port ${PORT}`); logger.info(`API Documentation available at http://localhost:${PORT}/api-docs`); }); // Graceful shutdown process.on('SIGTERM', () => { logger.info('SIGTERM received, shutting down gracefully'); server.close(() => { logger.info('Process terminated'); process.exit(0); }); }); process.on('SIGINT', () => { logger.info('SIGINT received, shutting down gracefully'); server.close(() => { logger.info('Process terminated'); process.exit(0); }); }); export default app; //# sourceMappingURL=index.js.map

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/Bichev/coinbase-chat-mcp'

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