#!/usr/bin/env node
import { Command } from "commander";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import dotenv from "dotenv";
import path from "path";
import fs from "fs";
import { configureWOOFiProForClient } from "./utils/auto-config.js";
// Hardcoded configuration values (non-sensitive, standard values)
const WOOFI_BASE_ENDPOINT = "https://api.orderly.org";
const WOOFI_BROKER_ID = "woofi_pro";
const WOOFI_CHAIN_ID = "42161"; // Arbitrum
// Load .env file from working directory (like CCXT does)
const envPath = path.resolve(process.cwd(), '.env');
const envExamplePath = path.resolve(process.cwd(), '.env.example');
try {
dotenv.config({ path: envPath });
console.error(`🔧 Loading environment from: ${envPath}`);
}
catch (error) {
console.error(`⚠️ Could not load .env from: ${envPath}`);
}
// Import endpoint modules
import * as Account from "./endpoints/account.js";
import * as Orders from "./endpoints/orders.js";
import * as Assets from "./endpoints/assets.js";
import * as Positions from "./endpoints/positions.js";
import * as Liquidations from "./endpoints/liquidations.js";
import * as Funding from "./endpoints/funding.js";
import { getSetupInstructions } from "./utils/setup.js";
import { validateConfig } from "./utils/auth.js";
// Simplified configuration schema - only user-specific values
export const configSchema = z.object({
WOOFI_API_KEY: z.string().describe("WOOFi API Key"),
WOOFI_SECRET_KEY: z.string().describe("WOOFi Secret Key"),
WOOFI_ACCOUNT_ID: z.string().optional().describe("WOOFi Account ID"),
});
// Global configuration storage
let globalConfig;
// Check if .env file exists and provide helpful setup instructions
function checkEnvSetup() {
const hasEnv = fs.existsSync(envPath);
const hasEnvExample = fs.existsSync(envExamplePath);
if (!hasEnv) {
console.error("");
console.error("🚨 No .env file found!");
console.error(`📂 Looking for: ${envPath}`);
console.error("");
if (hasEnvExample) {
console.error("💡 Quick setup:");
console.error(` cp .env.example .env`);
console.error(` # Edit .env with your API credentials`);
}
else {
console.error("💡 Create .env file with:");
console.error(" WOOFI_API_KEY=your_api_key");
console.error(" WOOFI_SECRET_KEY=your_secret_key");
console.error(" WOOFI_ACCOUNT_ID=your_account_id");
}
console.error("");
console.error("🔒 Security: API keys should ONLY be in .env files, never in MCP config!");
console.error("");
return false;
}
return true;
}
// Initialize configuration - only user-specific values from env
function initializeConfig() {
try {
// Check for .env file setup
const hasValidEnv = checkEnvSetup();
// Load from environment variables (loaded by dotenv)
const envConfig = {
WOOFI_API_KEY: process.env.WOOFI_API_KEY,
WOOFI_SECRET_KEY: process.env.WOOFI_SECRET_KEY,
WOOFI_ACCOUNT_ID: process.env.WOOFI_ACCOUNT_ID,
};
// Validate required environment variables
if (!envConfig.WOOFI_API_KEY || !envConfig.WOOFI_SECRET_KEY) {
console.error("❌ Missing required environment variables!");
console.error(" Required: WOOFI_API_KEY, WOOFI_SECRET_KEY");
if (hasValidEnv) {
console.error(" Check your .env file has these values set");
}
throw new Error("Missing required API credentials");
}
console.error("✅ Using environment variables from .env file");
globalConfig = configSchema.parse(envConfig);
// Set global environment variables for endpoint modules (including hardcoded values)
process.env.WOOFI_API_KEY = globalConfig.WOOFI_API_KEY;
process.env.WOOFI_SECRET_KEY = globalConfig.WOOFI_SECRET_KEY;
process.env.WOOFI_BASE_ENDPOINT = WOOFI_BASE_ENDPOINT;
process.env.WOOFI_CHAIN_ID = WOOFI_CHAIN_ID;
process.env.WOOFI_BROKER_ID = WOOFI_BROKER_ID;
if (globalConfig.WOOFI_ACCOUNT_ID)
process.env.WOOFI_ACCOUNT_ID = globalConfig.WOOFI_ACCOUNT_ID;
console.error(`✅ Configuration initialized - Endpoint: ${WOOFI_BASE_ENDPOINT}`);
console.error(`🔧 Using hardcoded values: Broker=${WOOFI_BROKER_ID}, Chain=${WOOFI_CHAIN_ID}`);
}
catch (error) {
console.error("❌ Configuration error:", error);
console.error("");
console.error("📖 For detailed setup instructions, visit:");
console.error(" https://github.com/DaunteEth/woofi-pro-mcp#setup");
console.error("");
throw error;
}
}
// Simple configuration validation (no circular dependency)
function validateAuthenticationConfig() {
try {
console.error("🔐 Validating authentication configuration...");
// Use existing validateConfig() from auth.ts - just checks env vars, no API calls
validateConfig();
console.error("✅ Authentication configuration validated");
console.error("🔄 Cache Bust:", new Date().toISOString());
console.error("📦 Auth Version: 1.0.1 - WORKING AUTHENTICATION");
return true;
}
catch (error) {
console.error("❌ Authentication configuration invalid:", error);
console.error("");
console.error(getSetupInstructions());
return false;
}
}
// CLI argument parsing and auto-configuration mode
async function handleCLICommands() {
const program = new Command();
program
.name('woofi-pro')
.description('WOOFi Pro MCP Server with auto-configuration')
.version('1.0.1');
program
.option('--client <client>', 'Target MCP client: claude, cursor, vscode, windsurf')
.option('--api-key <key>', 'WOOFi API Key for configuration')
.option('--secret-key <key>', 'WOOFi Secret Key for configuration')
.option('--account-id <id>', 'WOOFi Account ID for configuration')
.parse();
const options = program.opts();
// Check if user wants client-specific configuration
if (options.client || options.apiKey || options.secretKey || options.accountId) {
console.log('🚀 WOOFi Pro MCP Server - Client Configuration Mode');
console.log('');
// Validate all required options are provided
if (!options.client || !options.apiKey || !options.secretKey || !options.accountId) {
console.error('❌ Client configuration requires all four options:');
console.error(' --client <claude|cursor|vscode|windsurf>');
console.error(' --api-key <your_api_key>');
console.error(' --secret-key <your_secret_key>');
console.error(' --account-id <your_account_id>');
console.error('');
console.error('💡 Examples:');
console.error(' # Configure for Claude Desktop');
console.error(' npx -y git+https://github.com/DaunteEth/woofi-pro-mcp.git woofi-pro \\');
console.error(' --client claude --api-key=your_key --secret-key=your_secret --account-id=your_id');
console.error('');
console.error(' # Configure for Cursor IDE');
console.error(' npx -y git+https://github.com/DaunteEth/woofi-pro-mcp.git woofi-pro \\');
console.error(' --client cursor --api-key=your_key --secret-key=your_secret --account-id=your_id');
process.exit(1);
}
// Validate client type
const validClients = ['claude', 'cursor', 'vscode', 'windsurf'];
if (!validClients.includes(options.client)) {
console.error(`❌ Invalid client: ${options.client}`);
console.error(` Supported clients: ${validClients.join(', ')}`);
process.exit(1);
}
// Perform client-specific configuration
const credentials = {
apiKey: options.apiKey,
secretKey: options.secretKey,
accountId: options.accountId
};
try {
await configureWOOFiProForClient(options.client, credentials);
process.exit(0);
}
catch (error) {
console.error(`❌ Configuration failed for ${options.client}:`, error);
process.exit(1);
}
}
// No CLI options provided, continue with normal MCP server mode
return false;
}
async function main() {
try {
// Check for CLI auto-configuration mode first
const isAutoConfig = await handleCLICommands();
if (isAutoConfig)
return;
// Initialize config
initializeConfig();
// Validate authentication configuration
const authValid = validateAuthenticationConfig();
if (!authValid) {
console.error("⚠️ Server starting with limited functionality due to authentication issues");
}
const server = new McpServer({
name: "woofi-pro",
version: "1.0.1",
}, {
capabilities: {
tools: {},
},
});
console.error("🔧 Registering trading tools...");
// Account tools
server.tool("get_account_info", "Fetch account balances and fee tiers", {}, async () => {
const result = await Account.getAccountInfo();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_positions", "List open positions", {
symbol: z.string().optional().describe("Optional symbol to filter positions"),
}, async ({ symbol }) => {
const result = await Account.getAccountPositions();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
// Order tools
// === REGULAR ORDER MANAGEMENT TOOLS (10 tools) ===
server.tool("create_order", "Create a new regular order", {
symbol: z.string().describe("Trading symbol (e.g., PERP_ETH_USDC)"),
side: z.enum(["BUY", "SELL"]).describe("Order side"),
order_type: z.enum(["LIMIT", "MARKET", "IOC", "FOK", "POST_ONLY", "ASK", "BID"]).describe("Order type"),
order_price: z.number().optional().describe("Order price (required for LIMIT orders)"),
order_quantity: z.number().optional().describe("Order quantity"),
order_amount: z.number().optional().describe("Order amount in quote currency"),
client_order_id: z.string().optional().describe("Custom order ID (max 36 chars)"),
visible_quantity: z.number().optional().describe("Visible quantity on orderbook"),
reduce_only: z.boolean().optional().describe("Reduce only flag"),
slippage: z.number().optional().describe("Slippage tolerance for MARKET orders"),
order_tag: z.string().optional().describe("Order tag"),
level: z.number().optional().describe("Price level for ASK/BID orders"),
post_only_adjust: z.boolean().optional().describe("Adjust price for POST_ONLY orders"),
}, async (params) => {
const result = await Orders.createOrder(params);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("batch_create_orders", "Create multiple orders in batch", {
orders: z.array(z.object({
symbol: z.string().describe("Trading symbol"),
side: z.enum(["BUY", "SELL"]).describe("Order side"),
order_type: z.enum(["LIMIT", "MARKET", "IOC", "FOK", "POST_ONLY", "ASK", "BID"]).describe("Order type"),
order_price: z.number().optional().describe("Order price"),
order_quantity: z.number().optional().describe("Order quantity"),
order_amount: z.number().optional().describe("Order amount"),
client_order_id: z.string().optional().describe("Client order ID"),
visible_quantity: z.number().optional().describe("Visible quantity"),
reduce_only: z.boolean().optional().describe("Reduce only flag"),
slippage: z.number().optional().describe("Slippage tolerance"),
order_tag: z.string().optional().describe("Order tag"),
level: z.number().optional().describe("Price level"),
post_only_adjust: z.boolean().optional().describe("Post only adjust"),
})).describe("Array of orders to create"),
}, async ({ orders }) => {
const result = await Orders.batchCreateOrders({ orders });
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("edit_order", "Edit an existing order", {
order_id: z.string().describe("Order ID to edit"),
order_quantity: z.number().optional().describe("New order quantity"),
order_price: z.number().optional().describe("New order price"),
}, async (params) => {
const result = await Orders.editOrder(params);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("cancel_order", "Cancel an order by order ID", {
order_id: z.string().describe("Order ID to cancel"),
}, async ({ order_id }) => {
const result = await Orders.cancelOrder(order_id);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("cancel_order_by_client_id", "Cancel an order by client order ID", {
client_order_id: z.string().describe("Client order ID to cancel"),
}, async ({ client_order_id }) => {
const result = await Orders.cancelOrderByClientId(client_order_id);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("cancel_all_pending_orders", "Cancel all pending orders", {
symbol: z.string().optional().describe("Symbol to cancel orders for (optional)"),
}, async ({ symbol }) => {
const result = await Orders.cancelAllPendingOrders(symbol);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("cancel_all_after", "Cancel all orders after a timeout", {
timeout: z.number().describe("Timeout in milliseconds"),
}, async (params) => {
const result = await Orders.cancelAllAfter(params);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("get_orders", "Get orders with filtering options", {
symbol: z.string().optional().describe("Filter by symbol"),
status: z.enum(["NEW", "PARTIALLY_FILLED", "FILLED", "CANCELLED", "REJECTED", "INCOMPLETE", "COMPLETED"]).optional().describe("Filter by status"),
tag: z.string().optional().describe("Filter by tag"),
order_type: z.enum(["LIMIT", "MARKET", "IOC", "FOK", "POST_ONLY", "ASK", "BID"]).optional().describe("Filter by order type"),
order_tag: z.string().optional().describe("Filter by order tag"),
side: z.enum(["BUY", "SELL"]).optional().describe("Filter by side"),
start_t: z.number().optional().describe("Start time filter"),
end_t: z.number().optional().describe("End time filter"),
size: z.number().optional().describe("Page size"),
page: z.number().optional().describe("Page number"),
}, async (params) => {
const result = await Orders.getOrders(params);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("get_order_by_id", "Get order by order ID", {
order_id: z.string().describe("Order ID to retrieve"),
}, async ({ order_id }) => {
const result = await Orders.getOrderById(order_id);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("get_order_by_client_id", "Get order by client order ID", {
client_order_id: z.string().describe("Client order ID to retrieve"),
}, async ({ client_order_id }) => {
const result = await Orders.getOrderByClientId(client_order_id);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
// === ALGO ORDER MANAGEMENT TOOLS (8 tools) ===
server.tool("create_algo_order", "Create an algorithmic order (stop loss, take profit, etc.)", {
symbol: z.string().describe("Trading symbol"),
algo_type: z.enum(["STOP", "TP_SL", "POSITIONAL_TP_SL", "BRACKET", "TAKE_PROFIT", "STOP_LOSS"]).describe("Algorithm order type"),
type: z.enum(["LIMIT", "MARKET", "CLOSE_POSITION"]).optional().describe("Order type"),
quantity: z.number().optional().describe("Order quantity"),
side: z.enum(["BUY", "SELL"]).optional().describe("Order side"),
price: z.number().optional().describe("Order price"),
trigger_price: z.number().optional().describe("Trigger price"),
trigger_price_type: z.enum(["MARK_PRICE"]).optional().describe("Trigger price type"),
reduce_only: z.boolean().optional().describe("Reduce only flag"),
visible_quantity: z.boolean().optional().describe("Visible quantity flag"),
client_order_id: z.string().optional().describe("Custom order ID"),
order_tag: z.string().optional().describe("Order tag"),
child_orders: z.array(z.any()).optional().describe("Child orders for complex algos"),
}, async (params) => {
const result = await Orders.createAlgoOrder(params);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("edit_algo_order", "Edit an existing algo order", {
order_id: z.string().describe("Algo order ID to edit"),
quantity: z.number().optional().describe("New quantity"),
price: z.number().optional().describe("New price"),
trigger_price: z.number().optional().describe("New trigger price"),
}, async (params) => {
const result = await Orders.editAlgoOrder(params);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("cancel_algo_order", "Cancel an algo order by order ID", {
order_id: z.string().describe("Algo order ID to cancel"),
symbol: z.string().describe("Trading symbol for the algo order"),
}, async ({ order_id, symbol }) => {
const result = await Orders.cancelAlgoOrder(order_id, symbol);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("cancel_algo_order_by_client_id", "Cancel an algo order by client order ID", {
client_order_id: z.string().describe("Client order ID to cancel"),
symbol: z.string().describe("Trading symbol for the algo order"),
}, async ({ client_order_id, symbol }) => {
const result = await Orders.cancelAlgoOrderByClientId(client_order_id, symbol);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("cancel_all_pending_algo_orders", "Cancel all pending algo orders", {
symbol: z.string().optional().describe("Symbol to cancel algo orders for (optional)"),
}, async ({ symbol }) => {
const result = await Orders.cancelAllPendingAlgoOrders(symbol);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("get_algo_orders", "Get algo orders with filtering options", {
symbol: z.string().optional().describe("Filter by symbol"),
algo_type: z.enum(["STOP", "TP_SL", "POSITIONAL_TP_SL", "BRACKET"]).optional().describe("Filter by algo type"),
size: z.number().optional().describe("Page size"),
page: z.number().optional().describe("Page number"),
}, async (params) => {
const result = await Orders.getAlgoOrders(params);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("get_algo_order_by_id", "Get algo order by order ID", {
order_id: z.string().describe("Algo order ID to retrieve"),
}, async ({ order_id }) => {
const result = await Orders.getAlgoOrderById(order_id);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
server.tool("get_algo_order_by_client_id", "Get algo order by client order ID", {
client_order_id: z.string().describe("Client order ID to retrieve"),
}, async ({ client_order_id }) => {
const result = await Orders.getAlgoOrderByClientId(client_order_id);
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
});
// Asset tools
server.tool("get_asset_history", "Get asset transaction history", {}, async () => {
const result = await Assets.getAssetHistory();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_holdings", "Get asset holdings", {}, async () => {
const result = await Assets.getHoldings();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("create_withdraw_request", "Create a withdrawal request", {
token: z.string().describe("Token symbol"),
amount: z.string().describe("Amount to withdraw"),
address: z.string().describe("Withdrawal address"),
}, async (params) => {
const result = await Assets.createWithdrawRequest(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_settle_pnl_nonce", "Get settle PnL nonce", {}, async () => {
const result = await Assets.getSettlePnlNonce();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("request_pnl_settlement", "Request PnL settlement with EIP-712 signature", {
signature: z.string().describe("EIP-712 signature"),
userAddress: z.string().describe("User address"),
verifyingContract: z.string().describe("Verifying contract address"),
message: z.object({
brokerId: z.string(),
chainId: z.number(),
chainType: z.string(),
settleNonce: z.number(),
timestamp: z.number(),
}).describe("Message object for EIP-712 signature"),
}, async (params) => {
const result = await Assets.requestPnlSettlement(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_pnl_settlement_history", "Get PnL settlement history", {
symbol: z.string().optional().describe("Optional symbol filter"),
start_t: z.string().optional().describe("Start timestamp"),
end_t: z.string().optional().describe("End timestamp"),
page: z.string().optional().describe("Page number"),
size: z.string().optional().describe("Page size"),
}, async (params) => {
const result = await Assets.getPnlSettlementHistory(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("create_internal_transfer", "Create internal transfer between accounts", {
token: z.string().describe("Token symbol"),
amount: z.string().describe("Amount to transfer"),
fromAccountId: z.string().describe("Source account ID"),
toAccountId: z.string().describe("Destination account ID"),
}, async (params) => {
const result = await Assets.createInternalTransfer(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_internal_transfer_history", "Get internal transfer history", {
symbol: z.string().optional().describe("Optional symbol filter"),
start_t: z.string().optional().describe("Start timestamp"),
end_t: z.string().optional().describe("End timestamp"),
page: z.string().optional().describe("Page number"),
size: z.string().optional().describe("Page size"),
}, async (params) => {
const result = await Assets.getInternalTransferHistory(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
// Position tools
server.tool("get_all_positions", "Get all positions", {}, async () => {
const result = await Positions.getAllPositions();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_position_by_symbol", "Get position for specific symbol", {
symbol: z.string().describe("Trading symbol"),
}, async ({ symbol }) => {
const result = await Positions.getPositionBySymbol(symbol);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_position_history", "Get position history", {
symbol: z.string().optional().describe("Optional symbol to filter"),
limit: z.number().optional().describe("Optional limit for results"),
}, async ({ symbol, limit }) => {
const params = {};
if (symbol)
params.symbol = symbol;
if (limit)
params.limit = limit;
const result = await Positions.getPositionHistory(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
// Liquidation tools
server.tool("get_liquidated_positions", "Get public liquidated positions", {
symbol: z.string().optional().describe("Optional symbol filter"),
start_t: z.number().optional().describe("Start timestamp"),
end_t: z.number().optional().describe("End timestamp"),
page: z.number().optional().describe("Page number"),
size: z.number().optional().describe("Page size"),
}, async (params) => {
const result = await Liquidations.getLiquidatedPositions(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_liquidations", "Get user liquidation data", {
symbol: z.string().optional().describe("Optional symbol filter"),
start_t: z.number().optional().describe("Start timestamp"),
end_t: z.number().optional().describe("End timestamp"),
page: z.number().optional().describe("Page number"),
size: z.number().optional().describe("Page size"),
}, async (params) => {
const result = await Liquidations.getLiquidations(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_positions_under_liquidation", "Get positions under liquidation (public)", {
symbol: z.string().optional().describe("Optional symbol filter"),
start_t: z.number().optional().describe("Start timestamp"),
end_t: z.number().optional().describe("End timestamp"),
page: z.number().optional().describe("Page number"),
size: z.number().optional().describe("Page size"),
}, async (params) => {
const result = await Liquidations.getPositionsUnderLiquidation(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("claim_liquidated_positions", "Claim liquidated positions (POST with EIP-712 signature)", {
liquidation_id: z.number().describe("Liquidation ID"),
liquidator_id: z.string().describe("Liquidator ID"),
symbol: z.string().describe("Trading symbol"),
position_qty: z.string().describe("Position quantity"),
cost_position: z.string().describe("Cost position"),
liquidator_fee: z.string().describe("Liquidator fee"),
insurance_fund_fee: z.string().describe("Insurance fund fee"),
mark_price: z.string().describe("Mark price"),
sum_unitary_fundings: z.string().describe("Sum unitary fundings"),
liquidated_time: z.number().describe("Liquidated time"),
signature: z.string().describe("EIP-712 signature"),
userAddress: z.string().describe("User wallet address"),
verifyingContract: z.string().describe("Verifying contract address"),
message: z.object({
brokerId: z.string(),
chainId: z.number(),
chainType: z.string(),
liquidationId: z.number(),
liquidatorId: z.string(),
symbol: z.string(),
positionQty: z.string(),
costPosition: z.string(),
liquidatorFee: z.string(),
insuranceFundFee: z.string(),
markPrice: z.string(),
sumUnitaryFundings: z.string(),
liquidatedTime: z.number(),
timestamp: z.number(),
}).describe("EIP-712 message object"),
}, async (params) => {
const result = await Liquidations.claimLiquidatedPositions(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("claim_insurance_fund", "Claim insurance fund (POST with EIP-712 signature)", {
liquidation_id: z.number().describe("Liquidation ID"),
transfer_amount_to_insurance_fund: z.string().describe("Transfer amount to insurance fund"),
signature: z.string().describe("EIP-712 signature"),
userAddress: z.string().describe("User wallet address"),
verifyingContract: z.string().describe("Verifying contract address"),
message: z.object({
brokerId: z.string(),
chainId: z.number(),
chainType: z.string(),
liquidationId: z.number(),
transferAmountToInsuranceFund: z.string(),
timestamp: z.number(),
}).describe("EIP-712 message object"),
}, async (params) => {
const result = await Liquidations.claimInsuranceFund(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.tool("get_insurance_fund", "Get insurance fund details", {}, async () => {
const result = await Liquidations.getInsuranceFund();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
// Funding tools
server.tool("get_funding_fee_history", "Get funding fee history", {
symbol: z.string().optional().describe("Optional symbol to filter"),
start_t: z.string().optional().describe("Start timestamp"),
end_t: z.string().optional().describe("End timestamp"),
page: z.string().optional().describe("Page number"),
size: z.string().optional().describe("Page size"),
}, async ({ symbol, start_t, end_t, page, size }) => {
const params = {};
if (symbol)
params.symbol = symbol;
if (start_t)
params.start_t = start_t;
if (end_t)
params.end_t = end_t;
if (page)
params.page = page;
if (size)
params.size = size;
const result = await Funding.getFundingFeeHistory(params);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
console.error("✅ All 40 trading tools registered successfully");
// Use STDIO transport for Cursor IDE MCP integration
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("🟢 WOOFi Pro MCP Server running locally via STDIO with 40 tools enabled");
}
catch (error) {
console.error("❌ Server error:", error);
process.exit(1);
}
}
// Handle process events
process.on('SIGINT', () => {
console.error("🔴 Shutting down WOOFi Pro MCP Server");
process.exit(0);
});
process.on('uncaughtException', (error) => {
console.error("❌ Uncaught exception:", error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error("❌ Unhandled rejection at:", promise, 'reason:', reason);
process.exit(1);
});
// Start the server
main();