Skip to main content
Glama

Bybit MCP Server

index.ts33.7 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 { BybitService } from './bybit-service.js'; import { OrderRequest } from './types.js'; class BybitMCPServer { private server: Server; private bybitService: BybitService; constructor() { this.server = new Server( { name: 'bybit-mcp-node', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.bybitService = new BybitService(); this.setupToolHandlers(); this.setupErrorHandling(); } private setupErrorHandling(): void { this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers(): void { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'get_secret_key', description: 'Get secret key from environment variables', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_access_key', description: 'Get access key from environment variables', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_orderbook', description: 'Get orderbook data for a specific symbol', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, limit: { type: 'number', description: 'Number of orderbook entries to retrieve', default: 50, }, }, required: ['category', 'symbol'], }, }, { name: 'get_kline', description: 'Get K-line (candlestick) data', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, interval: { type: 'string', description: 'Time interval (1, 3, 5, 15, 30, 60, 120, 240, 360, 720, D, W, M)', }, start: { type: 'number', description: 'Start time in milliseconds', }, end: { type: 'number', description: 'End time in milliseconds', }, limit: { type: 'number', description: 'Number of records to retrieve', default: 200, }, }, required: ['category', 'symbol', 'interval'], }, }, { name: 'get_tickers', description: 'Get ticker information', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, }, required: ['category', 'symbol'], }, }, { name: 'get_wallet_balance', description: 'Get wallet balance', inputSchema: { type: 'object', properties: { accountType: { type: 'string', description: 'Account type (UNIFIED, CONTRACT, SPOT)', }, coin: { type: 'string', description: 'Coin symbol', }, }, required: ['accountType'], }, }, { name: 'get_positions', description: 'Get position information', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, }, required: ['category'], }, }, { name: 'place_order', description: 'Execute order', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, side: { type: 'string', description: 'Order direction (Buy, Sell)', }, orderType: { type: 'string', description: 'Order type (Market, Limit)', }, qty: { type: 'string', description: 'Order quantity', }, price: { type: 'string', description: 'Order price (for limit orders)', }, positionIdx: { type: 'string', description: 'Position index (1: Long, 2: Short)', }, timeInForce: { type: 'string', description: 'Time in force (GTC, IOC, FOK, PostOnly)', }, orderLinkId: { type: 'string', description: 'Order link ID', }, isLeverage: { type: 'number', description: 'Use leverage (0: No, 1: Yes)', }, orderFilter: { type: 'string', description: 'Order filter (Order, tpslOrder, StopOrder)', }, triggerPrice: { type: 'string', description: 'Trigger price', }, triggerBy: { type: 'string', description: 'Trigger basis', }, orderIv: { type: 'string', description: 'Order volatility', }, takeProfit: { type: 'string', description: 'Take profit price', }, stopLoss: { type: 'string', description: 'Stop loss price', }, tpTriggerBy: { type: 'string', description: 'Take profit trigger basis', }, slTriggerBy: { type: 'string', description: 'Stop loss trigger basis', }, tpLimitPrice: { type: 'string', description: 'Take profit limit price', }, slLimitPrice: { type: 'string', description: 'Stop loss limit price', }, tpOrderType: { type: 'string', description: 'Take profit order type (Market, Limit)', }, slOrderType: { type: 'string', description: 'Stop loss order type (Market, Limit)', }, }, required: ['category', 'symbol', 'side', 'orderType', 'qty'], }, }, { name: 'cancel_order', description: 'Cancel order', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, orderId: { type: 'string', description: 'Order ID', }, orderLinkId: { type: 'string', description: 'Order link ID', }, orderFilter: { type: 'string', description: 'Order filter', }, }, required: ['category', 'symbol'], }, }, { name: 'get_order_history', description: 'Get order history', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, orderId: { type: 'string', description: 'Order ID', }, orderLinkId: { type: 'string', description: 'Order link ID', }, orderFilter: { type: 'string', description: 'Order filter', }, orderStatus: { type: 'string', description: 'Order status', }, startTime: { type: 'number', description: 'Start time in milliseconds', }, endTime: { type: 'number', description: 'End time in milliseconds', }, limit: { type: 'number', description: 'Number of orders to retrieve', default: 50, }, }, required: ['category'], }, }, { name: 'get_open_orders', description: 'Get open orders', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, orderId: { type: 'string', description: 'Order ID', }, orderLinkId: { type: 'string', description: 'Order link ID', }, orderFilter: { type: 'string', description: 'Order filter', }, limit: { type: 'number', description: 'Number of orders to retrieve', default: 50, }, }, required: ['category'], }, }, { name: 'set_trading_stop', description: 'Set trading stop', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, takeProfit: { type: 'string', description: 'Take profit price', }, stopLoss: { type: 'string', description: 'Stop loss price', }, trailingStop: { type: 'string', description: 'Trailing stop', }, positionIdx: { type: 'number', description: 'Position index', }, }, required: ['category', 'symbol'], }, }, { name: 'set_margin_mode', description: 'Set margin mode', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, tradeMode: { type: 'number', description: 'Trading mode (0: Isolated, 1: Cross)', }, buyLeverage: { type: 'string', description: 'Buying leverage', }, sellLeverage: { type: 'string', description: 'Selling leverage', }, }, required: ['category', 'symbol', 'tradeMode', 'buyLeverage', 'sellLeverage'], }, }, { name: 'get_api_key_information', description: 'Get API key information', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_instruments_info', description: 'Get exchange information', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, status: { type: 'string', description: 'Status', }, baseCoin: { type: 'string', description: 'Base coin', }, }, required: ['category', 'symbol'], }, }, { name: 'validate_order_quantity', description: 'Validate order quantity against Bybit trading rules and get properly formatted quantity', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., BTCUSDT)', }, targetAmount: { type: 'number', description: 'Target amount in quote currency (e.g., 80 for $80 worth)', }, currentPrice: { type: 'number', description: 'Current price (optional, will fetch if not provided)', }, }, required: ['category', 'symbol', 'targetAmount'], }, }, { name: 'detect_position_mode', description: 'Detect current position mode (one-way vs hedge) and get recommended positionIdx for orders', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (spot, linear, inverse, etc.)', }, symbol: { type: 'string', description: 'Symbol (e.g., ETHUSDT) - optional, will check all positions if not provided', }, }, required: ['category'], }, }, { name: 'set_leverage', description: 'Set leverage for a specific symbol', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (linear, inverse)', }, symbol: { type: 'string', description: 'Symbol (e.g., ETHUSDT)', }, buyLeverage: { type: 'string', description: 'Buy leverage (e.g., "10" for 10x)', }, sellLeverage: { type: 'string', description: 'Sell leverage (e.g., "10" for 10x)', }, }, required: ['category', 'symbol', 'buyLeverage', 'sellLeverage'], }, }, { name: 'calculate_position_size', description: 'Calculate optimal position size based on risk management principles', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (linear, inverse)', }, symbol: { type: 'string', description: 'Symbol (e.g., ETHUSDT)', }, accountBalance: { type: 'number', description: 'Total account balance in USDT', }, riskPercentage: { type: 'number', description: 'Risk percentage per trade (e.g., 2 for 2%)', }, stopLossPrice: { type: 'number', description: 'Stop loss price', }, currentPrice: { type: 'number', description: 'Current price (optional, will fetch if not provided)', }, leverage: { type: 'number', description: 'Leverage to use (optional, defaults to 1x)', }, }, required: ['category', 'symbol', 'accountBalance', 'riskPercentage', 'stopLossPrice'], }, }, { name: 'calculate_trailing_stop', description: 'Calculate trailing stop loss with breakeven and profit protection', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (linear, inverse)', }, symbol: { type: 'string', description: 'Symbol (e.g., ETHUSDT)', }, entryPrice: { type: 'number', description: 'Entry price of the position', }, currentPrice: { type: 'number', description: 'Current market price', }, side: { type: 'string', description: 'Position side (Buy for long, Sell for short)', }, initialStopLoss: { type: 'number', description: 'Initial stop loss price', }, trailingDistance: { type: 'number', description: 'Trailing distance in price units', }, breakevenTrigger: { type: 'number', description: 'Price distance from entry to activate breakeven protection (optional)', }, profitProtectionTrigger: { type: 'number', description: 'Price distance from entry to activate profit protection (optional)', }, }, required: ['category', 'symbol', 'entryPrice', 'currentPrice', 'side', 'initialStopLoss', 'trailingDistance'], }, }, { name: 'place_order_with_trailing_stop', description: 'Place order with trailing stop loss', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category (linear, inverse)', }, symbol: { type: 'string', description: 'Symbol (e.g., ETHUSDT)', }, side: { type: 'string', description: 'Order side (Buy, Sell)', }, orderType: { type: 'string', description: 'Order type (Market, Limit)', }, qty: { type: 'string', description: 'Order quantity', }, price: { type: 'string', description: 'Order price (for limit orders)', }, trailingStop: { type: 'string', description: 'Trailing stop distance', }, activePrice: { type: 'string', description: 'Price at which trailing stop activates (optional)', }, positionIdx: { type: 'string', description: 'Position index (optional, auto-detected if not provided)', }, timeInForce: { type: 'string', description: 'Time in force (GTC, IOC, FOK)', }, orderLinkId: { type: 'string', description: 'Order link ID (optional)', }, }, required: ['category', 'symbol', 'side', 'orderType', 'qty', 'trailingStop'], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; // Type assertion for args const typedArgs = args as any; switch (name) { case 'get_secret_key': return { content: [ { type: 'text', text: process.env.SECRET_KEY || '', }, ], }; case 'get_access_key': return { content: [ { type: 'text', text: process.env.ACCESS_KEY || '', }, ], }; case 'get_orderbook': { const result = await this.bybitService.getOrderbook( typedArgs.category, typedArgs.symbol, typedArgs.limit ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'get_kline': { const result = await this.bybitService.getKline( typedArgs.category, typedArgs.symbol, typedArgs.interval, typedArgs.start, typedArgs.end, typedArgs.limit ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'get_tickers': { const result = await this.bybitService.getTickers(typedArgs.category, typedArgs.symbol); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'get_wallet_balance': { const result = await this.bybitService.getWalletBalance(typedArgs.accountType, typedArgs.coin); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'get_positions': { const result = await this.bybitService.getPositions(typedArgs.category, typedArgs.symbol); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'place_order': { const orderRequest: OrderRequest = { category: typedArgs.category, symbol: typedArgs.symbol, side: typedArgs.side, orderType: typedArgs.orderType, qty: typedArgs.qty, price: typedArgs.price, timeInForce: typedArgs.timeInForce, orderLinkId: typedArgs.orderLinkId, isLeverage: typedArgs.isLeverage, orderFilter: typedArgs.orderFilter, triggerPrice: typedArgs.triggerPrice, triggerBy: typedArgs.triggerBy, orderIv: typedArgs.orderIv, positionIdx: typedArgs.positionIdx, takeProfit: typedArgs.takeProfit, stopLoss: typedArgs.stopLoss, tpTriggerBy: typedArgs.tpTriggerBy, slTriggerBy: typedArgs.slTriggerBy, tpLimitPrice: typedArgs.tpLimitPrice, slLimitPrice: typedArgs.slLimitPrice, tpOrderType: typedArgs.tpOrderType, slOrderType: typedArgs.slOrderType, }; const result = await this.bybitService.placeOrder(orderRequest); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'cancel_order': { const result = await this.bybitService.cancelOrder( typedArgs.category, typedArgs.symbol, typedArgs.orderId, typedArgs.orderLinkId, typedArgs.orderFilter ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'get_order_history': { const result = await this.bybitService.getOrderHistory( typedArgs.category, typedArgs.symbol, typedArgs.orderId, typedArgs.orderLinkId, typedArgs.orderFilter, typedArgs.orderStatus, typedArgs.startTime, typedArgs.endTime, typedArgs.limit ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'get_open_orders': { const result = await this.bybitService.getOpenOrders( typedArgs.category, typedArgs.symbol, typedArgs.orderId, typedArgs.orderLinkId, typedArgs.orderFilter, typedArgs.limit ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'set_trading_stop': { const result = await this.bybitService.setTradingStop( typedArgs.category, typedArgs.symbol, typedArgs.takeProfit, typedArgs.stopLoss, typedArgs.trailingStop, typedArgs.positionIdx ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'set_margin_mode': { const result = await this.bybitService.setMarginMode( typedArgs.category, typedArgs.symbol, typedArgs.tradeMode, typedArgs.buyLeverage, typedArgs.sellLeverage ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'get_api_key_information': { const result = await this.bybitService.getApiKeyInformation(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'get_instruments_info': { const result = await this.bybitService.getInstrumentsInfo( typedArgs.category, typedArgs.symbol, typedArgs.status, typedArgs.baseCoin ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'validate_order_quantity': { const result = await this.bybitService.validateOrderQuantity( typedArgs.category, typedArgs.symbol, typedArgs.targetAmount, typedArgs.currentPrice ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'detect_position_mode': { const result = await this.bybitService.detectPositionMode( typedArgs.category, typedArgs.symbol ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'set_leverage': { const result = await this.bybitService.setLeverage( typedArgs.category, typedArgs.symbol, typedArgs.buyLeverage, typedArgs.sellLeverage ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'calculate_position_size': { const result = await this.bybitService.calculatePositionSize( typedArgs.category, typedArgs.symbol, typedArgs.accountBalance, typedArgs.riskPercentage, typedArgs.stopLossPrice, typedArgs.currentPrice, typedArgs.leverage ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'calculate_trailing_stop': { const result = await this.bybitService.calculateTrailingStop( typedArgs.category, typedArgs.symbol, typedArgs.entryPrice, typedArgs.currentPrice, typedArgs.side, typedArgs.initialStopLoss, typedArgs.trailingDistance, typedArgs.breakevenTrigger, typedArgs.profitProtectionTrigger ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'place_order_with_trailing_stop': { const result = await this.bybitService.placeOrderWithTrailingStop( typedArgs.category, typedArgs.symbol, typedArgs.side, typedArgs.orderType, typedArgs.qty, typedArgs.price, typedArgs.trailingStop, typedArgs.activePrice, typedArgs.positionIdx, typedArgs.timeInForce, typedArgs.orderLinkId ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${errorMessage}`); } }); } async run(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Bybit MCP Server (Node.js) running on stdio'); } } const server = new BybitMCPServer(); server.run().catch(console.error);

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/kondisettyravi/mcp-bybit-node'

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