Skip to main content
Glama
index.js42.1 kB
#!/usr/bin/env node import dotenv from "dotenv"; import path from "path"; import { fileURLToPath } from "url"; 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 axios from "axios"; import { Wallet } from "ethers"; // Load .env file from the same directory as this script const __dirname = path.dirname(fileURLToPath(import.meta.url)); dotenv.config({ path: path.join(__dirname, ".env") }); // Environment configuration const DERIVE_ENVIRONMENT = process.env.DERIVE_ENVIRONMENT || "mainnet"; const DERIVE_WALLET = process.env.DERIVE_WALLET || ""; const DERIVE_PRIVATE_KEY = process.env.DERIVE_PRIVATE_KEY || ""; // API endpoints const API_BASES = { mainnet: "https://api.lyra.finance", testnet: "https://api-demo.lyra.finance", }; const BASE_API_URL = API_BASES[DERIVE_ENVIRONMENT]; // Create wallet instance for signing let wallet = null; if (DERIVE_PRIVATE_KEY) { try { wallet = new Wallet(DERIVE_PRIVATE_KEY); } catch (error) { console.error("Failed to create wallet from private key:", error.message); } } // Utility function to sign messages for wallet-based authentication (async) async function signMessage(timestamp) { try { if (!wallet) return null; // Use Ethereum personal_sign (EIP-191) - this is what Derive expects return await wallet.signMessage(timestamp); } catch (error) { console.error("Signing error:", error.message); return null; } } // Utility function to make API requests async function makeRequest(method, params = {}, requiresAuth = false) { // Determine if this is a public or private endpoint // Method format: "public/get_something" or "private/get_something" const isPrivate = method.startsWith("private/"); const endpoint = isPrivate ? "/private/" : "/public/"; const methodName = method.replace(/^(public|private)\//, ""); const url = BASE_API_URL + endpoint + methodName; const headers = { "Content-Type": "application/json", }; // Add wallet-based authentication if required if (requiresAuth) { if (!DERIVE_WALLET) { throw new Error("DERIVE_WALLET environment variable is required for private endpoints"); } const timestamp = Date.now().toString(); const signature = await signMessage(timestamp); headers["X-LyraWallet"] = DERIVE_WALLET; headers["X-LyraTimestamp"] = timestamp; if (signature) { headers["X-LyraSignature"] = signature; } } try { const response = await axios.post(url, params, { headers }); // Check for error in response if (response.data.error) { throw new Error(`API Error: ${JSON.stringify(response.data.error)}`); } // Derive API returns results directly in the response return response.data.result || response.data; } catch (error) { if (error.response) { throw new Error(`API request failed: ${error.response.status} - ${JSON.stringify(error.response.data)}`); } throw error; } } // Create MCP server const server = new Server( { name: "mcp-derive", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // ========== PUBLIC MARKET DATA ENDPOINTS ========== { name: "get_currencies", description: "Get all available currencies (underlying assets) on the platform.", inputSchema: { type: "object", properties: {}, }, }, { name: "get_instruments", description: "Get all active instruments for a specified currency and type. Returns detailed information about tradeable assets including fees, constraints, and Greeks.", inputSchema: { type: "object", properties: { currency: { type: "string", description: "Underlying asset currency (e.g., ETH, BTC, SOL)", }, expired: { type: "boolean", description: "Include expired assets (capped to 1 week past)", default: false, }, instrument_type: { type: "string", description: "Asset type", enum: ["erc20", "option", "perp"], }, }, required: ["currency", "instrument_type"], }, }, { name: "get_ticker", description: "Get real-time ticker data for a single instrument (bid/ask, last price, 24h volume, Greeks for options).", inputSchema: { type: "object", properties: { instrument_name: { type: "string", description: "The instrument identifier (e.g., ETH-20250131-3000-C, BTC-PERP, USDC)", }, }, required: ["instrument_name"], }, }, { name: "get_tickers", description: "Get ticker information for multiple instruments at once. More efficient than get_ticker when querying many instruments.", inputSchema: { type: "object", properties: { currency: { type: "string", description: "Filter by currency (optional)", }, instrument_type: { type: "string", description: "Filter by instrument type", enum: ["erc20", "option", "perp"], }, }, }, }, { name: "get_orderbook", description: "Get real-time orderbook data for a specific instrument with bid/ask levels, depth, and spread.", inputSchema: { type: "object", properties: { instrument_name: { type: "string", description: "The instrument identifier", }, depth: { type: "number", description: "Number of price levels to return (default 10, max typically 100)", default: 10, }, }, required: ["instrument_name"], }, }, // ========== HISTORICAL DATA ENDPOINTS ========== { name: "get_trade_history", description: "Get tick-by-tick trade history with detailed trade information (price, size, buyer/seller, timestamp). Supports filtering and pagination.", inputSchema: { type: "object", properties: { instrument_name: { type: "string", description: "Filter by specific instrument name (optional)", }, currency: { type: "string", description: "Filter by currency (optional)", }, instrument_type: { type: "string", description: "Filter by instrument type", enum: ["erc20", "option", "perp"], }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds since Unix epoch (default 0)", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds since Unix epoch (default now)", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, trade_id: { type: "string", description: "Get specific trade by ID (overrides other filters)", }, tx_hash: { type: "string", description: "Get trade by on-chain transaction hash", }, tx_status: { type: "string", description: "Filter by transaction status", enum: ["settled", "reverted", "timed_out"], }, }, }, }, { name: "get_funding_rate_history", description: "Get historical funding rate data for perpetual contracts with timestamps and rates.", inputSchema: { type: "object", properties: { instrument_name: { type: "string", description: "Perpetual instrument (e.g., BTC-PERP, ETH-PERP) - optional to get all", }, currency: { type: "string", description: "Filter by currency (optional)", }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, }, }, { name: "get_spot_feed_history", description: "Get historical spot price feed data (index prices) with timestamps.", inputSchema: { type: "object", properties: { currency: { type: "string", description: "Currency to get spot feed for (e.g., BTC, ETH)", }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, required: ["currency"], }, }, { name: "get_option_settlement_history", description: "Get historical option settlement data (expiry, settlement prices, PnL).", inputSchema: { type: "object", properties: { currency: { type: "string", description: "Filter by currency (optional)", }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, }, }, { name: "get_liquidation_history", description: "Get liquidation events history across the platform.", inputSchema: { type: "object", properties: { currency: { type: "string", description: "Filter by currency (optional)", }, instrument_name: { type: "string", description: "Filter by instrument (optional)", }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, }, }, // ========== PRIVATE ACCOUNT ENDPOINTS ========== { name: "get_account", description: "Get account details including subaccounts, rate limits, fee structure, and account status. Requires authentication.", inputSchema: { type: "object", properties: { wallet: { type: "string", description: "Ethereum wallet address (defaults to DERIVE_WALLET env var)", }, }, }, }, { name: "get_subaccounts", description: "Get all subaccounts for a wallet with labels and creation timestamps. Requires authentication.", inputSchema: { type: "object", properties: { wallet: { type: "string", description: "Ethereum wallet address (defaults to DERIVE_WALLET env var)", }, }, }, }, { name: "get_balance", description: "Get account balance across all subaccounts including collaterals and open order margins. Requires authentication.", inputSchema: { type: "object", properties: { wallet: { type: "string", description: "Ethereum wallet address (defaults to DERIVE_WALLET env var)", }, }, }, }, { name: "get_positions", description: "Get all open positions for a subaccount including unrealized PnL, mark price, leverage, and liquidation price. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID to query positions for", }, currency: { type: "string", description: "Filter by currency (optional)", }, instrument_type: { type: "string", description: "Filter by instrument type", enum: ["erc20", "option", "perp"], }, }, required: ["subaccount_id"], }, }, { name: "get_collaterals", description: "Get collateral information for a subaccount including amounts, prices, and margin contributions. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID to query collaterals for", }, }, required: ["subaccount_id"], }, }, { name: "get_margin", description: "Get detailed margin information for a subaccount including maintenance margin, initial margin, and margin ratio. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID to query margin for", }, }, required: ["subaccount_id"], }, }, // ========== TRADING ENDPOINTS ========== { name: "get_open_orders", description: "Get all open orders for a subaccount with order status and details. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID to query orders for", }, currency: { type: "string", description: "Filter by currency (optional)", }, instrument_name: { type: "string", description: "Filter by instrument name (optional)", }, }, required: ["subaccount_id"], }, }, { name: "get_orders_history", description: "Get order history for a subaccount with filled, cancelled, and rejected orders. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID to query order history for", }, currency: { type: "string", description: "Filter by currency (optional)", }, instrument_name: { type: "string", description: "Filter by instrument name (optional)", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, required: ["subaccount_id"], }, }, { name: "place_order", description: "Place a new order (limit or market). Requires authentication and private key for signing.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID to place order on", }, instrument_name: { type: "string", description: "Instrument to trade (e.g., BTC-PERP, ETH-20250131-3000-C)", }, side: { type: "string", description: "Order side", enum: ["buy", "sell"], }, amount: { type: "string", description: "Order amount/quantity", }, price: { type: "string", description: "Limit price (for limit orders)", }, order_type: { type: "string", description: "Order type", enum: ["limit", "market"], default: "limit", }, reduce_only: { type: "boolean", description: "Reduce only flag (default false)", default: false, }, post_only: { type: "boolean", description: "Post only flag - maker only (default false)", default: false, }, label: { type: "string", description: "Custom order label (optional)", }, }, required: ["subaccount_id", "instrument_name", "side", "amount"], }, }, { name: "cancel_order", description: "Cancel an open order by order ID. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID that placed the order", }, order_id: { type: "string", description: "Order ID to cancel", }, }, required: ["subaccount_id", "order_id"], }, }, { name: "cancel_all_orders", description: "Cancel all open orders for a subaccount. Optionally filter by currency or instrument. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, currency: { type: "string", description: "Filter by currency (optional)", }, instrument_name: { type: "string", description: "Filter by instrument (optional)", }, }, required: ["subaccount_id"], }, }, { name: "replace_order", description: "Replace an existing order (cancel old and place new) in one atomic operation. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, order_id: { type: "string", description: "Order ID to replace", }, amount: { type: "string", description: "New order amount", }, price: { type: "string", description: "New order price", }, }, required: ["subaccount_id", "order_id", "amount", "price"], }, }, // ========== HISTORICAL ACCOUNT DATA ========== { name: "get_my_trades", description: "Get personal trade history with detailed trade information (side, price, fees, PnL). Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID to get trades for", }, instrument_name: { type: "string", description: "Filter by instrument (optional)", }, currency: { type: "string", description: "Filter by currency (optional)", }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, required: ["subaccount_id"], }, }, { name: "get_funding_history", description: "Get funding payments history (paid and received). Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, instrument_name: { type: "string", description: "Filter by perpetual instrument (optional)", }, currency: { type: "string", description: "Filter by currency (optional)", }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, required: ["subaccount_id"], }, }, { name: "get_deposit_history", description: "Get deposit history with transaction hashes and statuses. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, required: ["subaccount_id"], }, }, { name: "get_withdrawal_history", description: "Get withdrawal history with transaction hashes and statuses. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, from_timestamp: { type: "number", description: "Earliest timestamp in milliseconds", }, to_timestamp: { type: "number", description: "Latest timestamp in milliseconds", }, page: { type: "number", description: "Page number (default 1)", default: 1, }, page_size: { type: "number", description: "Results per page (default 100, max 1000)", default: 100, }, }, required: ["subaccount_id"], }, }, // ========== RFQ (REQUEST FOR QUOTE) ENDPOINTS ========== { name: "send_rfq", description: "Send a Request For Quote to get custom pricing from market makers. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, instrument_name: { type: "string", description: "Instrument to request quote for", }, side: { type: "string", description: "RFQ side", enum: ["buy", "sell"], }, amount: { type: "string", description: "Amount to request quote for", }, }, required: ["subaccount_id", "instrument_name", "side", "amount"], }, }, { name: "get_rfqs", description: "Get active RFQs for a subaccount with quotes received. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, }, required: ["subaccount_id"], }, }, { name: "execute_quote", description: "Execute a quoted trade at the quoted price. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, quote_id: { type: "string", description: "Quote ID to execute", }, }, required: ["subaccount_id", "quote_id"], }, }, // ========== RISK MANAGEMENT ========== { name: "get_liquidation_price", description: "Get current liquidation price for an open position. Requires authentication.", inputSchema: { type: "object", properties: { subaccount_id: { type: "number", description: "Subaccount ID", }, instrument_name: { type: "string", description: "Instrument with open position", }, }, required: ["subaccount_id", "instrument_name"], }, }, { name: "margin_watch", description: "Get margin watch information showing accounts approaching liquidation. Requires authentication.", inputSchema: { type: "object", properties: { wallet: { type: "string", description: "Wallet address (defaults to DERIVE_WALLET env var)", }, }, }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { let result; let requiresAuth = false; switch (name) { // ========== PUBLIC ENDPOINTS ========== case "get_currencies": result = await makeRequest("public/get_all_currencies"); break; case "get_instruments": result = await makeRequest("public/get_instruments", { currency: args.currency, expired: args.expired ?? false, instrument_type: args.instrument_type, }); break; case "get_ticker": result = await makeRequest("public/get_ticker", { instrument_name: args.instrument_name, }); break; case "get_tickers": const tickersParams = {}; if (args.currency) tickersParams.currency = args.currency; if (args.instrument_type) tickersParams.instrument_type = args.instrument_type; result = await makeRequest("public/get_tickers", tickersParams); break; case "get_orderbook": result = await makeRequest("public/get_orderbook", { instrument_name: args.instrument_name, depth: args.depth ?? 10, }); break; // ========== HISTORICAL DATA ========== case "get_trade_history": const tradeParams = {}; if (args.instrument_name) tradeParams.instrument_name = args.instrument_name; if (args.currency) tradeParams.currency = args.currency; if (args.instrument_type) tradeParams.instrument_type = args.instrument_type; if (args.from_timestamp) tradeParams.from_timestamp = args.from_timestamp; if (args.to_timestamp) tradeParams.to_timestamp = args.to_timestamp; tradeParams.page = args.page ?? 1; tradeParams.page_size = args.page_size ?? 100; if (args.trade_id) tradeParams.trade_id = args.trade_id; if (args.tx_hash) tradeParams.tx_hash = args.tx_hash; if (args.tx_status) tradeParams.tx_status = args.tx_status; result = await makeRequest("public/get_trade_history", tradeParams); break; case "get_funding_rate_history": const fundingParams = {}; if (args.instrument_name) fundingParams.instrument_name = args.instrument_name; if (args.currency) fundingParams.currency = args.currency; if (args.from_timestamp) fundingParams.from_timestamp = args.from_timestamp; if (args.to_timestamp) fundingParams.to_timestamp = args.to_timestamp; fundingParams.page = args.page ?? 1; fundingParams.page_size = args.page_size ?? 100; result = await makeRequest("public/get_funding_rate_history", fundingParams); break; case "get_spot_feed_history": result = await makeRequest("public/get_spot_feed_history", { currency: args.currency, from_timestamp: args.from_timestamp, to_timestamp: args.to_timestamp, page: args.page ?? 1, page_size: args.page_size ?? 100, }); break; case "get_option_settlement_history": const optionParams = {}; if (args.currency) optionParams.currency = args.currency; if (args.from_timestamp) optionParams.from_timestamp = args.from_timestamp; if (args.to_timestamp) optionParams.to_timestamp = args.to_timestamp; optionParams.page = args.page ?? 1; optionParams.page_size = args.page_size ?? 100; result = await makeRequest("public/get_option_settlement_history", optionParams); break; case "get_liquidation_history": const liquidationParams = {}; if (args.currency) liquidationParams.currency = args.currency; if (args.instrument_name) liquidationParams.instrument_name = args.instrument_name; if (args.from_timestamp) liquidationParams.from_timestamp = args.from_timestamp; if (args.to_timestamp) liquidationParams.to_timestamp = args.to_timestamp; liquidationParams.page = args.page ?? 1; liquidationParams.page_size = args.page_size ?? 100; result = await makeRequest("public/get_liquidation_history", liquidationParams); break; // ========== PRIVATE ACCOUNT ENDPOINTS ========== case "get_account": requiresAuth = true; result = await makeRequest("private/get_account", { wallet: args.wallet || DERIVE_WALLET, }, requiresAuth); break; case "get_subaccounts": requiresAuth = true; result = await makeRequest("private/get_subaccounts", { wallet: args.wallet || DERIVE_WALLET, }, requiresAuth); break; case "get_balance": requiresAuth = true; result = await makeRequest("private/get_all_portfolios", { wallet: args.wallet || DERIVE_WALLET, }, requiresAuth); break; case "get_positions": requiresAuth = true; const positionsParams = { subaccount_id: args.subaccount_id, }; if (args.currency) positionsParams.currency = args.currency; if (args.instrument_type) positionsParams.instrument_type = args.instrument_type; result = await makeRequest("private/get_positions", positionsParams, requiresAuth); break; case "get_collaterals": requiresAuth = true; result = await makeRequest("private/get_collaterals", { subaccount_id: args.subaccount_id, }, requiresAuth); break; case "get_margin": requiresAuth = true; result = await makeRequest("private/get_margin", { subaccount_id: args.subaccount_id, }, requiresAuth); break; // ========== TRADING ENDPOINTS ========== case "get_open_orders": requiresAuth = true; const openOrdersParams = { subaccount_id: args.subaccount_id, }; if (args.currency) openOrdersParams.currency = args.currency; if (args.instrument_name) openOrdersParams.instrument_name = args.instrument_name; result = await makeRequest("private/get_open_orders", openOrdersParams, requiresAuth); break; case "get_orders_history": requiresAuth = true; const ordersHistoryParams = { subaccount_id: args.subaccount_id, page: args.page ?? 1, page_size: args.page_size ?? 100, }; if (args.currency) ordersHistoryParams.currency = args.currency; if (args.instrument_name) ordersHistoryParams.instrument_name = args.instrument_name; result = await makeRequest("private/get_order_history", ordersHistoryParams, requiresAuth); break; case "place_order": requiresAuth = true; const orderParams = { subaccount_id: args.subaccount_id, instrument_name: args.instrument_name, side: args.side, amount: args.amount, order_type: args.order_type ?? "limit", reduce_only: args.reduce_only ?? false, post_only: args.post_only ?? false, }; if (args.price) orderParams.price = args.price; if (args.label) orderParams.label = args.label; result = await makeRequest("private/order", orderParams, requiresAuth); break; case "cancel_order": requiresAuth = true; result = await makeRequest("private/cancel", { subaccount_id: args.subaccount_id, order_id: args.order_id, }, requiresAuth); break; case "cancel_all_orders": requiresAuth = true; const cancelAllParams = { subaccount_id: args.subaccount_id, }; if (args.currency) cancelAllParams.currency = args.currency; if (args.instrument_name) cancelAllParams.instrument_name = args.instrument_name; result = await makeRequest("private/cancel_all", cancelAllParams, requiresAuth); break; case "replace_order": requiresAuth = true; result = await makeRequest("private/replace", { subaccount_id: args.subaccount_id, order_id: args.order_id, amount: args.amount, price: args.price, }, requiresAuth); break; // ========== HISTORICAL ACCOUNT DATA ========== case "get_my_trades": requiresAuth = true; const myTradesParams = { subaccount_id: args.subaccount_id, page: args.page ?? 1, page_size: args.page_size ?? 100, }; if (args.instrument_name) myTradesParams.instrument_name = args.instrument_name; if (args.currency) myTradesParams.currency = args.currency; if (args.from_timestamp) myTradesParams.from_timestamp = args.from_timestamp; if (args.to_timestamp) myTradesParams.to_timestamp = args.to_timestamp; result = await makeRequest("private/get_trade_history", myTradesParams, requiresAuth); break; case "get_funding_history": requiresAuth = true; const fundingHistoryParams = { subaccount_id: args.subaccount_id, page: args.page ?? 1, page_size: args.page_size ?? 100, }; if (args.instrument_name) fundingHistoryParams.instrument_name = args.instrument_name; if (args.currency) fundingHistoryParams.currency = args.currency; if (args.from_timestamp) fundingHistoryParams.from_timestamp = args.from_timestamp; if (args.to_timestamp) fundingHistoryParams.to_timestamp = args.to_timestamp; result = await makeRequest("private/get_funding_history", fundingHistoryParams, requiresAuth); break; case "get_deposit_history": requiresAuth = true; const depositParams = { subaccount_id: args.subaccount_id, page: args.page ?? 1, page_size: args.page_size ?? 100, }; if (args.from_timestamp) depositParams.from_timestamp = args.from_timestamp; if (args.to_timestamp) depositParams.to_timestamp = args.to_timestamp; result = await makeRequest("private/get_deposit_history", depositParams, requiresAuth); break; case "get_withdrawal_history": requiresAuth = true; const withdrawalParams = { subaccount_id: args.subaccount_id, page: args.page ?? 1, page_size: args.page_size ?? 100, }; if (args.from_timestamp) withdrawalParams.from_timestamp = args.from_timestamp; if (args.to_timestamp) withdrawalParams.to_timestamp = args.to_timestamp; result = await makeRequest("private/get_withdrawal_history", withdrawalParams, requiresAuth); break; // ========== RFQ ENDPOINTS ========== case "send_rfq": requiresAuth = true; result = await makeRequest("private/send_rfq", { subaccount_id: args.subaccount_id, instrument_name: args.instrument_name, side: args.side, amount: args.amount, }, requiresAuth); break; case "get_rfqs": requiresAuth = true; result = await makeRequest("private/get_rfqs", { subaccount_id: args.subaccount_id, }, requiresAuth); break; case "execute_quote": requiresAuth = true; result = await makeRequest("private/execute_quote", { subaccount_id: args.subaccount_id, quote_id: args.quote_id, }, requiresAuth); break; // ========== RISK MANAGEMENT ========== case "get_liquidation_price": requiresAuth = true; // This endpoint might not exist directly, using get_positions as alternative const positions = await makeRequest("private/get_positions", { subaccount_id: args.subaccount_id, }, requiresAuth); // Filter to the specific instrument const positionsResult = positions?.positions || []; const position = positionsResult.find(p => p.instrument_name === args.instrument_name); if (position) { result = { instrument_name: args.instrument_name, liquidation_price: position.liquidation_price, mark_price: position.mark_price, amount: position.amount, }; } else { throw new Error(`No position found for ${args.instrument_name}`); } break; case "margin_watch": requiresAuth = true; result = await makeRequest("public/margin_watch", { wallet: args.wallet || DERIVE_WALLET, }, requiresAuth); break; default: throw new Error(`Unknown tool: ${name}`); } return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error: ${error.message}`, }, ], isError: true, }; } }); // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Derive MCP Server running on stdio"); console.error(`Environment: ${DERIVE_ENVIRONMENT}`); console.error(`API URL: ${BASE_API_URL}`); console.error(`Wallet: ${DERIVE_WALLET || "Not configured"}`); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

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/solenyaresearch0000/derive-MCP'

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