--- id: "MCP-BINANCE-CONNECTOR-SPEC" title: "Especificacion MCP Binance Connector" type: "Technical Specification" project: "trading-platform" version: "1.0.0" created_date: "2026-01-04" updated_date: "2026-01-04" author: "Orquestador Agent - Trading Platform" --- # MCP Binance Connector - Especificacion Tecnica **Version:** 1.0.0 **Fecha:** 2026-01-04 **Modulo:** OQI-010-llm-trading-integration **Puerto:** 3606 --- ## Tabla de Contenidos 1. [Vision General](#vision-general) 2. [Arquitectura](#arquitectura) 3. [MCP Tools Specification](#mcp-tools-specification) 4. [Seguridad](#seguridad) 5. [Configuracion](#configuracion) 6. [Implementacion](#implementacion) 7. [Testing](#testing) --- ## Vision General ### Proposito El MCP Binance Connector expone las funcionalidades de Binance como herramientas MCP (Model Context Protocol), permitiendo que los agentes LLM interactuen con el exchange de forma segura y controlada. ### Capacidades | Categoria | Funcionalidades | |-----------|-----------------| | **Market Data** | Precios, order book, velas, 24h stats | | **Account** | Balance, posiciones, historial | | **Trading** | Ordenes market/limit, cancelacion | | **Futures** | Posiciones, leverage, funding | ### Integracion con LLM Agent ``` ┌─────────────────────────────────────────────────────────────────┐ │ LLM TRADING AGENT │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ MCP ORCHESTRATOR │ │ │ │ │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ │ │ MCP MT4 │ │ MCP BINANCE │ │ │ │ │ │ Connector │ │ Connector │ │ │ │ │ │ :3605 │ │ :3606 │ │ │ │ │ └────────┬─────────┘ └────────┬─────────┘ │ │ │ └────────────┼─────────────────────────┼────────────────────┘ │ │ │ │ │ └───────────────┼─────────────────────────┼───────────────────────┘ │ │ ▼ ▼ ┌───────────────┐ ┌───────────────┐ │ MetaAPI │ │ Binance │ │ (MT4/MT5) │ │ Exchange │ └───────────────┘ └───────────────┘ ``` --- ## Arquitectura ### Estructura del Proyecto ``` apps/mcp-binance-connector/ ├── src/ │ ├── index.ts # Entry point & MCP server │ ├── config.ts # Configuration management │ │ │ ├── services/ │ │ ├── binance-client.ts # CCXT wrapper for Binance │ │ ├── rate-limiter.ts # Rate limiting service │ │ └── websocket-manager.ts # WebSocket connections │ │ │ ├── tools/ │ │ ├── index.ts # Tool registry │ │ ├── market.ts # Market data tools │ │ ├── account.ts # Account info tools │ │ ├── orders.ts # Order management tools │ │ ├── positions.ts # Position management (futures) │ │ └── utils.ts # Utility tools │ │ │ ├── middleware/ │ │ ├── auth.ts # API key validation │ │ ├── risk-check.ts # Pre-trade risk validation │ │ └── logging.ts # Request/response logging │ │ │ └── types/ │ ├── binance.ts # Binance-specific types │ └── mcp.ts # MCP protocol types │ ├── tests/ │ ├── unit/ │ │ ├── market.test.ts │ │ └── orders.test.ts │ └── integration/ │ └── binance-api.test.ts │ ├── docs/ │ ├── ARCHITECTURE.md │ └── MCP-TOOLS-SPEC.md │ ├── Dockerfile ├── package.json ├── tsconfig.json ├── .env.example └── README.md ``` ### Dependencias Principales ```json { "dependencies": { "@modelcontextprotocol/sdk": "^0.6.0", "ccxt": "^4.0.0", "express": "^4.18.2", "zod": "^3.22.0", "redis": "^4.6.0", "winston": "^3.11.0", "axios": "^1.6.0" }, "devDependencies": { "typescript": "^5.3.0", "ts-node-dev": "^2.0.0", "jest": "^29.7.0", "@types/node": "^20.10.0" } } ``` --- ## MCP Tools Specification ### Tool Categories #### 1. Market Data Tools (Read-Only, Low Risk) ```typescript // tools/market.ts /** * Tool: binance_get_ticker * Obtiene precio actual y estadisticas 24h de un simbolo */ export const getTickerTool: MCPTool = { name: "binance_get_ticker", description: "Obtiene el precio actual y estadisticas de 24h para un simbolo de trading", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading (ej: BTCUSDT, ETHUSDT)", pattern: "^[A-Z]{2,10}$" } }, required: ["symbol"] }, handler: async ({ symbol }) => { const ticker = await binanceClient.fetchTicker(symbol); return { symbol: ticker.symbol, price: ticker.last, bid: ticker.bid, ask: ticker.ask, high24h: ticker.high, low24h: ticker.low, volume24h: ticker.baseVolume, change24h: ticker.percentage, timestamp: ticker.timestamp }; } }; /** * Tool: binance_get_orderbook * Obtiene el order book con profundidad especificada */ export const getOrderbookTool: MCPTool = { name: "binance_get_orderbook", description: "Obtiene el order book (bids y asks) con la profundidad especificada", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading" }, limit: { type: "number", description: "Profundidad del orderbook (5, 10, 20, 50, 100)", enum: [5, 10, 20, 50, 100], default: 20 } }, required: ["symbol"] }, handler: async ({ symbol, limit = 20 }) => { const orderbook = await binanceClient.fetchOrderBook(symbol, limit); return { symbol, bids: orderbook.bids.slice(0, 10), asks: orderbook.asks.slice(0, 10), spread: orderbook.asks[0][0] - orderbook.bids[0][0], spreadPercentage: ((orderbook.asks[0][0] - orderbook.bids[0][0]) / orderbook.bids[0][0]) * 100, timestamp: orderbook.timestamp }; } }; /** * Tool: binance_get_klines * Obtiene velas historicas (OHLCV) */ export const getKlinesTool: MCPTool = { name: "binance_get_klines", description: "Obtiene velas historicas (OHLCV) para analisis tecnico", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading" }, interval: { type: "string", description: "Intervalo temporal", enum: ["1m", "5m", "15m", "30m", "1h", "4h", "1d", "1w"], default: "5m" }, limit: { type: "number", description: "Numero de velas (max 500)", default: 100, maximum: 500 } }, required: ["symbol"] }, handler: async ({ symbol, interval = "5m", limit = 100 }) => { const ohlcv = await binanceClient.fetchOHLCV(symbol, interval, undefined, limit); return { symbol, interval, candles: ohlcv.map(c => ({ timestamp: c[0], open: c[1], high: c[2], low: c[3], close: c[4], volume: c[5] })), count: ohlcv.length }; } }; /** * Tool: binance_get_trades * Obtiene trades recientes */ export const getRecentTradesTool: MCPTool = { name: "binance_get_trades", description: "Obtiene los trades mas recientes del mercado", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading" }, limit: { type: "number", description: "Numero de trades (max 100)", default: 50, maximum: 100 } }, required: ["symbol"] }, handler: async ({ symbol, limit = 50 }) => { const trades = await binanceClient.fetchTrades(symbol, undefined, limit); return { symbol, trades: trades.map(t => ({ id: t.id, price: t.price, amount: t.amount, side: t.side, timestamp: t.timestamp })), count: trades.length }; } }; ``` #### 2. Account Tools (Read-Only, Medium Risk) ```typescript // tools/account.ts /** * Tool: binance_get_account * Obtiene balance y estado de la cuenta */ export const getAccountTool: MCPTool = { name: "binance_get_account", description: "Obtiene el balance y estado de la cuenta de trading", inputSchema: { type: "object", properties: {}, required: [] }, handler: async () => { const balance = await binanceClient.fetchBalance(); // Filtrar solo activos con balance > 0 const nonZeroBalances = Object.entries(balance.total) .filter(([_, amount]) => amount > 0) .map(([asset, total]) => ({ asset, free: balance.free[asset], locked: balance.used[asset], total })); return { accountType: "SPOT", balances: nonZeroBalances, totalUSDT: await calculateTotalUSDT(nonZeroBalances), canTrade: true, canWithdraw: true, updateTime: Date.now() }; } }; /** * Tool: binance_get_open_orders * Obtiene ordenes abiertas */ export const getOpenOrdersTool: MCPTool = { name: "binance_get_open_orders", description: "Obtiene todas las ordenes abiertas (pendientes de ejecucion)", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo especifico (opcional, si no se especifica retorna todos)" } }, required: [] }, handler: async ({ symbol }) => { const orders = await binanceClient.fetchOpenOrders(symbol); return { orders: orders.map(o => ({ id: o.id, symbol: o.symbol, side: o.side, type: o.type, price: o.price, amount: o.amount, filled: o.filled, remaining: o.remaining, status: o.status, createdAt: o.timestamp })), count: orders.length }; } }; /** * Tool: binance_get_trade_history * Obtiene historial de trades ejecutados */ export const getTradeHistoryTool: MCPTool = { name: "binance_get_trade_history", description: "Obtiene el historial de trades ejecutados por el usuario", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading" }, limit: { type: "number", description: "Numero de trades (max 100)", default: 50, maximum: 100 } }, required: ["symbol"] }, handler: async ({ symbol, limit = 50 }) => { const trades = await binanceClient.fetchMyTrades(symbol, undefined, limit); return { symbol, trades: trades.map(t => ({ id: t.id, orderId: t.order, side: t.side, price: t.price, amount: t.amount, cost: t.cost, fee: t.fee, timestamp: t.timestamp })), totalPnL: calculatePnL(trades), count: trades.length }; } }; ``` #### 3. Order Management Tools (High Risk, Requires Confirmation) ```typescript // tools/orders.ts /** * Tool: binance_create_order * Crea una orden de trading * ALTO RIESGO - Requiere confirmacion explicita */ export const createOrderTool: MCPTool = { name: "binance_create_order", description: "Crea una orden de compra o venta. ALTO RIESGO - Asegurate de validar con el usuario antes de ejecutar.", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading (ej: BTCUSDT)" }, side: { type: "string", description: "Direccion de la orden", enum: ["buy", "sell"] }, type: { type: "string", description: "Tipo de orden", enum: ["market", "limit", "stop_loss", "take_profit"], default: "market" }, amount: { type: "number", description: "Cantidad a comprar/vender" }, price: { type: "number", description: "Precio limite (requerido para ordenes limit)" }, stopPrice: { type: "number", description: "Precio de activacion para stop orders" } }, required: ["symbol", "side", "amount"] }, riskLevel: "HIGH", requiresConfirmation: true, handler: async ({ symbol, side, type = "market", amount, price, stopPrice }) => { // Pre-trade risk check const riskCheck = await performRiskCheck({ symbol, side, amount, price }); if (!riskCheck.allowed) { return { success: false, error: "RISK_CHECK_FAILED", details: riskCheck.reason }; } try { let order; if (type === "market") { order = await binanceClient.createMarketOrder(symbol, side, amount); } else if (type === "limit" && price) { order = await binanceClient.createLimitOrder(symbol, side, amount, price); } else if (type === "stop_loss" && stopPrice) { order = await binanceClient.createOrder(symbol, "stop_loss", side, amount, undefined, { stopPrice }); } return { success: true, order: { id: order.id, symbol: order.symbol, side: order.side, type: order.type, price: order.price || order.average, amount: order.amount, filled: order.filled, status: order.status, timestamp: order.timestamp } }; } catch (error) { return { success: false, error: error.message }; } } }; /** * Tool: binance_cancel_order * Cancela una orden pendiente */ export const cancelOrderTool: MCPTool = { name: "binance_cancel_order", description: "Cancela una orden pendiente", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading" }, orderId: { type: "string", description: "ID de la orden a cancelar" } }, required: ["symbol", "orderId"] }, riskLevel: "MEDIUM", handler: async ({ symbol, orderId }) => { try { const result = await binanceClient.cancelOrder(orderId, symbol); return { success: true, cancelledOrder: { id: result.id, symbol: result.symbol, status: "CANCELLED" } }; } catch (error) { return { success: false, error: error.message }; } } }; /** * Tool: binance_cancel_all_orders * Cancela todas las ordenes de un simbolo */ export const cancelAllOrdersTool: MCPTool = { name: "binance_cancel_all_orders", description: "Cancela todas las ordenes pendientes de un simbolo", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading" } }, required: ["symbol"] }, riskLevel: "MEDIUM", handler: async ({ symbol }) => { try { const result = await binanceClient.cancelAllOrders(symbol); return { success: true, cancelledCount: result.length }; } catch (error) { return { success: false, error: error.message }; } } }; ``` #### 4. Futures Tools (High Risk) ```typescript // tools/positions.ts /** * Tool: binance_get_futures_positions * Obtiene posiciones abiertas en futuros */ export const getFuturesPositionsTool: MCPTool = { name: "binance_get_futures_positions", description: "Obtiene posiciones abiertas en Binance Futures", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo especifico (opcional)" } }, required: [] }, handler: async ({ symbol }) => { // Cambiar a cliente de futuros const positions = await binanceFuturesClient.fetchPositions(symbol); const activePositions = positions.filter(p => parseFloat(p.contracts) !== 0 ); return { positions: activePositions.map(p => ({ symbol: p.symbol, side: parseFloat(p.contracts) > 0 ? 'LONG' : 'SHORT', size: Math.abs(parseFloat(p.contracts)), entryPrice: parseFloat(p.entryPrice), markPrice: parseFloat(p.markPrice), unrealizedPnL: parseFloat(p.unrealizedPnl), leverage: p.leverage, liquidationPrice: parseFloat(p.liquidationPrice), marginType: p.marginType })), count: activePositions.length }; } }; /** * Tool: binance_set_leverage * Configura el leverage para un simbolo */ export const setLeverageTool: MCPTool = { name: "binance_set_leverage", description: "Configura el nivel de leverage para un simbolo en Futures", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de trading" }, leverage: { type: "number", description: "Nivel de leverage (1-125)", minimum: 1, maximum: 125 } }, required: ["symbol", "leverage"] }, riskLevel: "HIGH", handler: async ({ symbol, leverage }) => { // Validar leverage maximo if (leverage > 20) { return { success: false, error: "LEVERAGE_TOO_HIGH", message: "Leverage mayor a 20x no recomendado por politica de riesgo" }; } try { await binanceFuturesClient.setLeverage(leverage, symbol); return { success: true, symbol, leverage, message: `Leverage configurado a ${leverage}x` }; } catch (error) { return { success: false, error: error.message }; } } }; /** * Tool: binance_close_position * Cierra una posicion de futuros */ export const closePositionTool: MCPTool = { name: "binance_close_position", description: "Cierra completamente una posicion de futuros. ALTO RIESGO.", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de la posicion a cerrar" } }, required: ["symbol"] }, riskLevel: "HIGH", requiresConfirmation: true, handler: async ({ symbol }) => { try { // Obtener posicion actual const positions = await binanceFuturesClient.fetchPositions(symbol); const position = positions.find(p => p.symbol === symbol && parseFloat(p.contracts) !== 0 ); if (!position) { return { success: false, error: "NO_POSITION", message: `No hay posicion abierta para ${symbol}` }; } const size = Math.abs(parseFloat(position.contracts)); const side = parseFloat(position.contracts) > 0 ? 'sell' : 'buy'; // Crear orden de cierre const order = await binanceFuturesClient.createMarketOrder( symbol, side, size, undefined, { reduceOnly: true } ); return { success: true, closedPosition: { symbol, closedSize: size, closedSide: side === 'sell' ? 'LONG' : 'SHORT', realizedPnL: parseFloat(position.unrealizedPnl), orderId: order.id } }; } catch (error) { return { success: false, error: error.message }; } } }; ``` #### 5. Utility Tools ```typescript // tools/utils.ts /** * Tool: binance_get_exchange_info * Obtiene informacion del exchange */ export const getExchangeInfoTool: MCPTool = { name: "binance_get_exchange_info", description: "Obtiene informacion del exchange: simbolos disponibles, limites, etc.", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo especifico (opcional)" } }, required: [] }, handler: async ({ symbol }) => { const markets = await binanceClient.loadMarkets(); if (symbol) { const market = markets[symbol]; if (!market) { return { error: `Symbol ${symbol} not found` }; } return { symbol: market.symbol, base: market.base, quote: market.quote, precision: market.precision, limits: market.limits, active: market.active }; } // Retornar resumen general const activeMarkets = Object.values(markets).filter(m => m.active); return { totalMarkets: activeMarkets.length, topMarkets: activeMarkets .filter(m => m.quote === 'USDT') .slice(0, 20) .map(m => m.symbol) }; } }; /** * Tool: binance_get_funding_rate * Obtiene funding rate actual (futures) */ export const getFundingRateTool: MCPTool = { name: "binance_get_funding_rate", description: "Obtiene el funding rate actual para un simbolo de futuros", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Simbolo de futuros (ej: BTCUSDT)" } }, required: ["symbol"] }, handler: async ({ symbol }) => { const funding = await binanceFuturesClient.fetchFundingRate(symbol); return { symbol, fundingRate: funding.fundingRate, fundingTimestamp: funding.fundingTimestamp, nextFundingTime: funding.nextFundingTimestamp, annualized: funding.fundingRate * 3 * 365 * 100 // Aproximado anualizado }; } }; ``` --- ## Seguridad ### Niveles de Riesgo | Nivel | Herramientas | Requiere Confirmacion | |-------|--------------|----------------------| | **LOW** | get_ticker, get_klines, get_orderbook | No | | **MEDIUM** | get_account, cancel_order | No | | **HIGH** | create_order, close_position, set_leverage | SI | ### Middleware de Autorizacion ```typescript // middleware/auth.ts import { Request, Response, NextFunction } from 'express'; export const authMiddleware = async ( req: Request, res: Response, next: NextFunction ) => { // 1. Verificar API key del MCP client const mcpKey = req.headers['x-mcp-api-key']; if (!mcpKey || mcpKey !== process.env.MCP_API_KEY) { return res.status(401).json({ error: 'Invalid MCP API key' }); } // 2. Verificar que Binance API keys estan configuradas if (!process.env.BINANCE_API_KEY || !process.env.BINANCE_API_SECRET) { return res.status(500).json({ error: 'Binance API not configured' }); } next(); }; ``` ### Pre-Trade Risk Check ```typescript // middleware/risk-check.ts interface RiskCheckParams { symbol: string; side: 'buy' | 'sell'; amount: number; price?: number; } export const performRiskCheck = async (params: RiskCheckParams) => { const { symbol, side, amount, price } = params; // 1. Verificar balance suficiente const balance = await binanceClient.fetchBalance(); const quoteAsset = symbol.replace(/.*?(USDT|BUSD|USDC)$/, '$1'); const available = balance.free[quoteAsset] || 0; const orderValue = price ? amount * price : amount * (await getCurrentPrice(symbol)); if (side === 'buy' && available < orderValue) { return { allowed: false, reason: `Insufficient balance. Required: ${orderValue}, Available: ${available}` }; } // 2. Verificar tamano maximo de orden const maxOrderValue = parseFloat(process.env.MAX_ORDER_VALUE_USDT || '1000'); if (orderValue > maxOrderValue) { return { allowed: false, reason: `Order value ${orderValue} exceeds maximum ${maxOrderValue} USDT` }; } // 3. Verificar limites diarios const dailyVolume = await getDailyTradingVolume(); const maxDailyVolume = parseFloat(process.env.MAX_DAILY_VOLUME_USDT || '10000'); if (dailyVolume + orderValue > maxDailyVolume) { return { allowed: false, reason: `Daily volume limit reached. Current: ${dailyVolume}, Limit: ${maxDailyVolume}` }; } return { allowed: true }; }; ``` --- ## Configuracion ### Variables de Entorno ```bash # .env.example # === MCP Server === PORT=3606 MCP_API_KEY=your_mcp_api_key_here # === Binance API === BINANCE_API_KEY=your_binance_api_key BINANCE_API_SECRET=your_binance_api_secret # === Network === BINANCE_TESTNET=true # Usar testnet por defecto BINANCE_FUTURES_TESTNET=true # === Risk Limits === MAX_ORDER_VALUE_USDT=1000 MAX_DAILY_VOLUME_USDT=10000 MAX_LEVERAGE=20 MAX_POSITION_SIZE_PCT=5 # === Logging === LOG_LEVEL=info LOG_FILE=logs/mcp-binance.log # === Redis (para rate limiting) === REDIS_URL=redis://localhost:6379 ``` ### Configuracion de CCXT ```typescript // config.ts import ccxt from 'ccxt'; export const createBinanceClient = () => { const isTestnet = process.env.BINANCE_TESTNET === 'true'; return new ccxt.binance({ apiKey: process.env.BINANCE_API_KEY, secret: process.env.BINANCE_API_SECRET, sandbox: isTestnet, options: { defaultType: 'spot', adjustForTimeDifference: true, }, enableRateLimit: true, rateLimit: 100 }); }; export const createBinanceFuturesClient = () => { const isTestnet = process.env.BINANCE_FUTURES_TESTNET === 'true'; return new ccxt.binance({ apiKey: process.env.BINANCE_API_KEY, secret: process.env.BINANCE_API_SECRET, sandbox: isTestnet, options: { defaultType: 'future', adjustForTimeDifference: true, }, enableRateLimit: true, rateLimit: 100 }); }; ``` --- ## Implementacion ### Entry Point ```typescript // src/index.ts import express from 'express'; import { Server } from '@modelcontextprotocol/sdk/server'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio'; import { createBinanceClient, createBinanceFuturesClient } from './config'; import { registerTools } from './tools'; import { authMiddleware } from './middleware/auth'; import { loggingMiddleware } from './middleware/logging'; import logger from './utils/logger'; const app = express(); const PORT = process.env.PORT || 3606; // Middleware app.use(express.json()); app.use(loggingMiddleware); // Health check app.get('/health', (req, res) => { res.json({ status: 'healthy', service: 'mcp-binance-connector', version: '1.0.0', testnet: process.env.BINANCE_TESTNET === 'true', timestamp: new Date().toISOString() }); }); // MCP Server setup const server = new Server({ name: 'trading-binance-mcp', version: '1.0.0' }, { capabilities: { tools: {} } }); // Initialize clients const binanceClient = createBinanceClient(); const binanceFuturesClient = createBinanceFuturesClient(); // Register all tools registerTools(server, binanceClient, binanceFuturesClient); // Tool execution endpoint (HTTP fallback) app.post('/tools/:toolName', authMiddleware, async (req, res) => { const { toolName } = req.params; const params = req.body; try { const result = await executeToolByName(toolName, params); res.json(result); } catch (error) { logger.error(`Tool execution failed: ${toolName}`, error); res.status(500).json({ error: error.message }); } }); // List available tools app.get('/tools', (req, res) => { res.json({ tools: getAllToolDefinitions() }); }); // Start HTTP server app.listen(PORT, () => { logger.info(`MCP Binance Connector running on port ${PORT}`); logger.info(`Testnet mode: ${process.env.BINANCE_TESTNET === 'true'}`); }); // Start MCP stdio transport const transport = new StdioServerTransport(); server.connect(transport).catch((error) => { logger.error('MCP transport connection failed', error); }); export default app; ``` ### Dockerfile ```dockerfile # Dockerfile FROM node:20-alpine WORKDIR /app # Install dependencies COPY package*.json ./ RUN npm ci --only=production # Copy source COPY . . # Build TypeScript RUN npm run build # Environment ENV NODE_ENV=production ENV PORT=3606 EXPOSE 3606 # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3606/health || exit 1 CMD ["node", "dist/index.js"] ``` --- ## Testing ### Unit Tests ```typescript // tests/unit/market.test.ts import { getTickerTool, getKlinesTool } from '../../src/tools/market'; describe('Market Tools', () => { describe('getTickerTool', () => { it('should return ticker data for valid symbol', async () => { const result = await getTickerTool.handler({ symbol: 'BTCUSDT' }); expect(result).toHaveProperty('symbol', 'BTCUSDT'); expect(result).toHaveProperty('price'); expect(result.price).toBeGreaterThan(0); }); it('should handle invalid symbol', async () => { const result = await getTickerTool.handler({ symbol: 'INVALID' }); expect(result).toHaveProperty('error'); }); }); describe('getKlinesTool', () => { it('should return OHLCV data', async () => { const result = await getKlinesTool.handler({ symbol: 'BTCUSDT', interval: '5m', limit: 10 }); expect(result.candles).toHaveLength(10); expect(result.candles[0]).toHaveProperty('open'); expect(result.candles[0]).toHaveProperty('high'); expect(result.candles[0]).toHaveProperty('low'); expect(result.candles[0]).toHaveProperty('close'); expect(result.candles[0]).toHaveProperty('volume'); }); }); }); ``` ### Integration Tests ```typescript // tests/integration/binance-api.test.ts import request from 'supertest'; import app from '../../src/index'; describe('MCP Binance Connector API', () => { const API_KEY = process.env.MCP_API_KEY; describe('GET /health', () => { it('should return healthy status', async () => { const res = await request(app).get('/health'); expect(res.status).toBe(200); expect(res.body.status).toBe('healthy'); }); }); describe('POST /tools/binance_get_ticker', () => { it('should return ticker data', async () => { const res = await request(app) .post('/tools/binance_get_ticker') .set('x-mcp-api-key', API_KEY) .send({ symbol: 'BTCUSDT' }); expect(res.status).toBe(200); expect(res.body).toHaveProperty('symbol', 'BTCUSDT'); expect(res.body).toHaveProperty('price'); }); }); describe('POST /tools/binance_create_order', () => { it('should reject without confirmation', async () => { const res = await request(app) .post('/tools/binance_create_order') .set('x-mcp-api-key', API_KEY) .send({ symbol: 'BTCUSDT', side: 'buy', amount: 0.001 }); // En testnet deberia funcionar, pero verificar risk check expect(res.status).toBe(200); }); }); }); ``` --- ## Metricas y Monitoring | Metrica | Target | Alerta | |---------|--------|--------| | Latency | <200ms | >500ms | | Error Rate | <1% | >5% | | Uptime | 99.9% | <99% | | Rate Limit Usage | <80% | >90% | --- **Documento Generado:** 2026-01-04 **Autor:** Orquestador Agent - Trading Platform **Version:** 1.0.0