Skip to main content
Glama

Aster Finance MCP Server

by questflowai
index.ts27.1 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import axios from 'axios'; import crypto from 'crypto'; const API_KEY = process.env.ASTER_API_KEY; const API_SECRET = process.env.ASTER_API_SECRET; class AsterServer { private server: Server; private axiosInstance; constructor() { this.server = new Server( { name: 'aster-mcp-server', version: '0.1.0', }, { capabilities: { resources: {}, tools: {}, }, } ); this.axiosInstance = axios.create({ baseURL: 'https://fapi.asterdex.com', }); this.setupToolHandlers(); this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // Market Data { name: 'ping', description: 'Test connectivity to the Rest API.', inputSchema: { type: 'object', properties: {} } }, { name: 'time', description: 'Get the current server time.', inputSchema: { type: 'object', properties: {} } }, { name: 'exchangeInfo', description: 'Get current exchange trading rules and symbol information.', inputSchema: { type: 'object', properties: {} } }, { name: 'depth', description: 'Get the order book for a symbol.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol, e.g., BTCUSDT' }, limit: { type: 'number', description: 'Number of results. Default 500, max 1000.' }, }, required: ['symbol'], }, }, { name: 'trades', description: 'Get recent market trades.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, limit: { type: 'number', description: 'Number of results. Default 500, max 1000.' }, }, required: ['symbol'], }, }, { name: 'historicalTrades', description: 'Get older market historical trades.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, limit: { type: 'number', description: 'Number of results. Default 500, max 1000.' }, fromId: { type: 'number', description: 'TradeId to fetch from.' }, }, required: ['symbol'], }, }, { name: 'aggTrades', description: 'Get compressed, aggregate market trades.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, fromId: { type: 'number', description: 'ID to get aggregate trades from INCLUSIVE.' }, startTime: { type: 'number', description: 'Timestamp in ms to get aggregate trades from INCLUSIVE.' }, endTime: { type: 'number', description: 'Timestamp in ms to get aggregate trades until INCLUSIVE.' }, limit: { type: 'number', description: 'Number of results. Default 500, max 1000.' }, }, required: ['symbol'], }, }, { name: 'klines', description: 'Get Kline/candlestick bars for a symbol.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, interval: { type: 'string', description: 'Kline interval (e.g., 1m, 5m, 1h, 1d)' }, startTime: { type: 'number', description: 'Start time in ms' }, endTime: { type: 'number', description: 'End time in ms' }, limit: { type: 'number', description: 'Number of results. Default 500, max 1500.' }, }, required: ['symbol', 'interval'], }, }, { name: 'indexPriceKlines', description: 'Kline/candlestick bars for the index price of a pair.', inputSchema: { type: 'object', properties: { pair: { type: 'string', description: 'Trading pair, e.g., BTCUSDT' }, interval: { type: 'string', description: 'Kline interval' }, startTime: { type: 'number', description: 'Start time in ms' }, endTime: { type: 'number', description: 'End time in ms' }, limit: { type: 'number', description: 'Number of results. Default 500, max 1500.' }, }, required: ['pair', 'interval'], }, }, { name: 'markPriceKlines', description: 'Kline/candlestick bars for the mark price of a symbol.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, interval: { type: 'string', description: 'Kline interval' }, startTime: { type: 'number', description: 'Start time in ms' }, endTime: { type: 'number', description: 'End time in ms' }, limit: { type: 'number', description: 'Number of results. Default 500, max 1500.' }, }, required: ['symbol', 'interval'], }, }, { name: 'premiumIndex', description: 'Get Mark Price and Funding Rate.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, }, }, }, { name: 'fundingRate', description: 'Get funding rate history.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, startTime: { type: 'number', description: 'Start time in ms' }, endTime: { type: 'number', description: 'End time in ms' }, limit: { type: 'number', description: 'Number of results. Default 100, max 1000.' }, }, }, }, { name: 'fundingInfo', description: 'Get funding rate config.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, }, }, }, { name: 'ticker_24hr', description: '24 hour rolling window price change statistics.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, }, }, }, { name: 'ticker_price', description: 'Latest price for a symbol or symbols.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, }, }, }, { name: 'ticker_bookTicker', description: 'Best price/qty on the order book for a symbol or symbols.', inputSchema: { type: 'object', properties: { symbol: { type: 'string', description: 'Trading symbol' }, }, }, }, // Account/Trades { name: 'setPositionMode', description: "Change user's position mode (Hedge Mode or One-way Mode).", inputSchema: { type: 'object', properties: { dualSidePosition: { type: 'string', description: '"true" for Hedge Mode; "false" for One-way Mode' }, }, required: ['dualSidePosition'], }, }, { name: 'getPositionMode', description: "Get user's position mode.", inputSchema: { type: 'object', properties: {} } }, { name: 'setMultiAssetsMode', description: "Change user's Multi-Assets mode.", inputSchema: { type: 'object', properties: { multiAssetsMargin: { type: 'string', description: '"true" for Multi-Assets Mode; "false" for Single-Asset Mode' }, }, required: ['multiAssetsMargin'], }, }, { name: 'getMultiAssetsMode', description: "Get user's Multi-Assets mode.", inputSchema: { type: 'object', properties: {} } }, { name: 'placeOrder', description: 'Send in a new order.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, side: { type: 'string', enum: ['BUY', 'SELL'] }, positionSide: { type: 'string', enum: ['BOTH', 'LONG', 'SHORT'] }, type: { type: 'string', enum: ['LIMIT', 'MARKET', 'STOP', 'STOP_MARKET', 'TAKE_PROFIT', 'TAKE_PROFIT_MARKET', 'TRAILING_STOP_MARKET'] }, timeInForce: { type: 'string', enum: ['GTC', 'IOC', 'FOK', 'GTX'] }, quantity: { type: 'number' }, price: { type: 'number' }, stopPrice: { type: 'number' }, }, required: ['symbol', 'side', 'type'], }, }, { name: 'placeBatchOrders', description: 'Place multiple orders.', inputSchema: { type: 'object', properties: { batchOrders: { type: 'array', items: { type: 'object', properties: { symbol: { type: 'string' }, side: { type: 'string', enum: ['BUY', 'SELL'] }, type: { type: 'string' }, quantity: { type: 'number' }, price: { type: 'number' }, }, required: ['symbol', 'side', 'type', 'quantity'], }, }, }, required: ['batchOrders'], }, }, { name: 'transferAsset', description: 'Transfer between futures and spot.', inputSchema: { type: 'object', properties: { asset: { type: 'string' }, amount: { type: 'number' }, clientTranId: { type: 'string' }, kindType: { type: 'string', enum: ['FUTURE_SPOT', 'SPOT_FUTURE'] }, }, required: ['asset', 'amount', 'clientTranId', 'kindType'], }, }, { name: 'queryOrder', description: "Check an order's status.", inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, orderId: { type: 'number' }, origClientOrderId: { type: 'string' }, }, required: ['symbol'], }, }, { name: 'cancelOrder', description: 'Cancel an active order.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, orderId: { type: 'number' }, origClientOrderId: { type: 'string' }, }, required: ['symbol'], }, }, { name: 'cancelAllOpenOrders', description: 'Cancel all open orders on a symbol.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, }, required: ['symbol'], }, }, { name: 'cancelBatchOrders', description: 'Cancel multiple orders.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, orderIdList: { type: 'array', items: { type: 'number' } }, origClientOrderIdList: { type: 'array', items: { type: 'string' } }, }, required: ['symbol'], }, }, { name: 'countdownCancelAll', description: 'Auto-cancel all open orders.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, countdownTime: { type: 'number', description: 'Countdown time in milliseconds.' }, }, required: ['symbol', 'countdownTime'], }, }, { name: 'queryOpenOrder', description: 'Query current open order.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, orderId: { type: 'number' }, origClientOrderId: { type: 'string' }, }, required: ['symbol'], }, }, { name: 'getAllOpenOrders', description: 'Get all open orders on a symbol.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, }, }, }, { name: 'getAllOrders', description: 'Get all account orders; active, canceled, or filled.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, orderId: { type: 'number' }, startTime: { type: 'number' }, endTime: { type: 'number' }, limit: { type: 'number' }, }, required: ['symbol'], }, }, { name: 'getBalance', description: 'Get futures account balance.', inputSchema: { type: 'object', properties: {} } }, { name: 'getAccountInfo', description: 'Get current account information.', inputSchema: { type: 'object', properties: {} } }, { name: 'setLeverage', description: "Change user's initial leverage.", inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, leverage: { type: 'number', minimum: 1, maximum: 125 }, }, required: ['symbol', 'leverage'], }, }, { name: 'setMarginType', description: 'Change margin type.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, marginType: { type: 'string', enum: ['ISOLATED', 'CROSSED'] }, }, required: ['symbol', 'marginType'], }, }, { name: 'modifyPositionMargin', description: 'Modify isolated position margin.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, positionSide: { type: 'string', enum: ['BOTH', 'LONG', 'SHORT'] }, amount: { type: 'number' }, type: { type: 'number', enum: [1, 2], description: '1: Add, 2: Reduce' }, }, required: ['symbol', 'amount', 'type'], }, }, { name: 'getPositionMarginHistory', description: 'Get position margin change history.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, type: { type: 'number', enum: [1, 2] }, startTime: { type: 'number' }, endTime: { type: 'number' }, limit: { type: 'number' }, }, required: ['symbol'], }, }, { name: 'getPositionInfo', description: 'Get current position information.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, }, }, }, { name: 'getTradeList', description: 'Get trades for a specific account and symbol.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, startTime: { type: 'number' }, endTime: { type: 'number' }, fromId: { type: 'number' }, limit: { type: 'number' }, }, required: ['symbol'], }, }, { name: 'getIncomeHistory', description: 'Get income history.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, incomeType: { type: 'string' }, startTime: { type: 'number' }, endTime: { type: 'number' }, limit: { type: 'number' }, }, }, }, { name: 'getLeverageBrackets', description: 'Get notional and leverage brackets.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, }, }, }, { name: 'getAdlQuantile', description: 'Get Position ADL Quantile Estimation.', inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, }, }, }, { name: 'getForceOrders', description: "Get user's force orders.", inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, autoCloseType: { type: 'string', enum: ['LIQUIDATION', 'ADL'] }, startTime: { type: 'number' }, endTime: { type: 'number' }, limit: { type: 'number' }, }, }, }, { name: 'getCommissionRate', description: "Get user's commission rate.", inputSchema: { type: 'object', properties: { symbol: { type: 'string' }, }, required: ['symbol'], }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const makeRequest = async (method: 'GET' | 'POST' | 'DELETE', path: string, params: any, isSigned = false) => { try { let config: any = { method, url: path, }; if (isSigned) { if (!API_KEY || !API_SECRET) { throw new McpError(ErrorCode.InvalidRequest, 'API_KEY and API_SECRET must be configured.'); } params.timestamp = Date.now(); const queryString = new URLSearchParams(params).toString(); const signature = crypto.createHmac('sha256', API_SECRET).update(queryString).digest('hex'); params.signature = signature; config.headers = { 'X-MBX-APIKEY': API_KEY }; } if (method === 'GET' || method === 'DELETE') { config.params = params; } else { // POST config.data = new URLSearchParams(params).toString(); config.headers = { ...config.headers, 'Content-Type': 'application/x-www-form-urlencoded' }; } const response = await this.axiosInstance.request(config); return { content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }] }; } catch (error) { if (axios.isAxiosError(error)) { throw new McpError( ErrorCode.InternalError, `Aster API error: ${error.response?.data?.msg || error.message}` ); } throw error; } }; switch (name) { // Market Data Endpoints case 'ping': return makeRequest('GET', '/fapi/v1/ping', {}); case 'time': return makeRequest('GET', '/fapi/v1/time', {}); case 'exchangeInfo': return makeRequest('GET', '/fapi/v1/exchangeInfo', {}); case 'depth': return makeRequest('GET', '/fapi/v1/depth', args); case 'trades': return makeRequest('GET', '/fapi/v1/trades', args); case 'historicalTrades': return makeRequest('GET', '/fapi/v1/historicalTrades', args); case 'aggTrades': return makeRequest('GET', '/fapi/v1/aggTrades', args); case 'klines': return makeRequest('GET', '/fapi/v1/klines', args); case 'indexPriceKlines': return makeRequest('GET', '/fapi/v1/indexPriceKlines', args); case 'markPriceKlines': return makeRequest('GET', '/fapi/v1/markPriceKlines', args); case 'premiumIndex': return makeRequest('GET', '/fapi/v1/premiumIndex', args); case 'fundingRate': return makeRequest('GET', '/fapi/v1/fundingRate', args); case 'fundingInfo': return makeRequest('GET', '/fapi/v1/fundingInfo', args); case 'ticker_24hr': return makeRequest('GET', '/fapi/v1/ticker/24hr', args); case 'ticker_price': return makeRequest('GET', '/fapi/v1/ticker/price', args); case 'ticker_bookTicker': return makeRequest('GET', '/fapi/v1/ticker/bookTicker', args); // Account/Trades Endpoints (requires signing) case 'setPositionMode': return makeRequest('POST', '/fapi/v1/positionSide/dual', args, true); case 'getPositionMode': return makeRequest('GET', '/fapi/v1/positionSide/dual', args, true); case 'setMultiAssetsMode': return makeRequest('POST', '/fapi/v1/multiAssetsMargin', args, true); case 'getMultiAssetsMode': return makeRequest('GET', '/fapi/v1/multiAssetsMargin', args, true); case 'placeOrder': return makeRequest('POST', '/fapi/v1/order', args, true); case 'placeBatchOrders': if (!args || !args.batchOrders) { throw new McpError(ErrorCode.InvalidParams, 'batchOrders is required.'); } const batchOrdersStr = JSON.stringify(args.batchOrders); return makeRequest('POST', '/fapi/v1/batchOrders', { ...args, batchOrders: batchOrdersStr }, true); case 'transferAsset': return makeRequest('POST', '/fapi/v1/asset/wallet/transfer', args, true); case 'queryOrder': return makeRequest('GET', '/fapi/v1/order', args, true); case 'cancelOrder': return makeRequest('DELETE', '/fapi/v1/order', args, true); case 'cancelAllOpenOrders': return makeRequest('DELETE', '/fapi/v1/allOpenOrders', args, true); case 'cancelBatchOrders': if (!args || (!args.orderIdList && !args.origClientOrderIdList)) { throw new McpError(ErrorCode.InvalidParams, 'Either orderIdList or origClientOrderIdList is required.'); } const params = { ...args }; if (params.orderIdList) { params.orderIdList = JSON.stringify(params.orderIdList); } if (params.origClientOrderIdList) { params.origClientOrderIdList = JSON.stringify(params.origClientOrderIdList); } return makeRequest('DELETE', '/fapi/v1/batchOrders', params, true); case 'countdownCancelAll': return makeRequest('POST', '/fapi/v1/countdownCancelAll', args, true); case 'queryOpenOrder': return makeRequest('GET', '/fapi/v1/openOrder', args, true); case 'getAllOpenOrders': return makeRequest('GET', '/fapi/v1/openOrders', args, true); case 'getAllOrders': return makeRequest('GET', '/fapi/v1/allOrders', args, true); case 'getBalance': return makeRequest('GET', '/fapi/v2/balance', args, true); case 'getAccountInfo': return makeRequest('GET', '/fapi/v4/account', args, true); case 'setLeverage': return makeRequest('POST', '/fapi/v1/leverage', args, true); case 'setMarginType': return makeRequest('POST', '/fapi/v1/marginType', args, true); case 'modifyPositionMargin': return makeRequest('POST', '/fapi/v1/positionMargin', args, true); case 'getPositionMarginHistory': return makeRequest('GET', '/fapi/v1/positionMargin/history', args, true); case 'getPositionInfo': return makeRequest('GET', '/fapi/v2/positionRisk', args, true); case 'getTradeList': return makeRequest('GET', '/fapi/v1/userTrades', args, true); case 'getIncomeHistory': return makeRequest('GET', '/fapi/v1/income', args, true); case 'getLeverageBrackets': return makeRequest('GET', '/fapi/v1/leverageBracket', args, true); case 'getAdlQuantile': return makeRequest('GET', '/fapi/v1/adlQuantile', args, true); case 'getForceOrders': return makeRequest('GET', '/fapi/v1/forceOrders', args, true); case 'getCommissionRate': return makeRequest('GET', '/fapi/v1/commissionRate', args, true); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Aster MCP server running on stdio'); } } const server = new AsterServer(); server.run().catch(console.error);

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/questflowai/aster-mcp-server'

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