UNPKG

@tamago-labs/okx-dex-mcp

Version:

MCP server for OKX DEX Analytics - comprehensive DEX aggregator analytics with multi-chain support

865 lines (851 loc) 29.2 kB
#!/usr/bin/env node "use strict"; // src/index.ts var import_config6 = require("dotenv/config"); // src/config.ts var import_zod = require("zod"); var SUPPORTED_CHAINS = { // EVM Chains ETHEREUM: "1", OPTIMISM: "10", BSC: "56", OKT: "66", UNI_CHAIN: "130", SONIC: "146", X_LAYER: "196", POLYGON: "137", ARBITRUM: "42161", AVALANCHE: "43114", ZKSYNC: "324", POLYGON_ZKEVM: "1101", BASE: "8453", LINEA: "59144", FANTOM: "250", MANTLE: "5000", CONFLUX: "1030", METIS: "1088", MERLIN: "4200", BLAST: "81457", MANTA: "169", SCROLL: "534352", CRONOS: "25", ZETA: "7000", // Non-EVM Chains TRON: "195", SOLANA: "501", SUI: "784", TON: "607" }; var QuoteParamsSchema = import_zod.z.object({ chainId: import_zod.z.string().describe("Chain ID"), fromTokenAddress: import_zod.z.string().describe("Source token contract address"), toTokenAddress: import_zod.z.string().describe("Destination token contract address"), amount: import_zod.z.string().describe("Amount to swap in smallest unit"), slippage: import_zod.z.string().default("0.005").describe("Slippage tolerance (e.g., '0.005' for 0.5%)") }); var ChainInfoParamsSchema = import_zod.z.object({ chainId: import_zod.z.string().optional().describe("Chain ID to get information for") }); var LiquidityParamsSchema = import_zod.z.object({ chainId: import_zod.z.string().describe("Chain ID to get liquidity for") }); var CompareQuotesParamsSchema = import_zod.z.object({ fromTokenAddress: import_zod.z.string().describe("Source token contract address"), toTokenAddress: import_zod.z.string().describe("Destination token contract address"), amount: import_zod.z.string().describe("Amount to swap in smallest unit"), chainIds: import_zod.z.array(import_zod.z.string()).describe("List of chain IDs to compare") }); function validateEnvironment() { try { const required = ["OKX_API_KEY", "OKX_SECRET_KEY", "OKX_API_PASSPHRASE", "OKX_PROJECT_ID"]; const missing = required.filter((key) => !process.env[key]); if (missing.length > 0) { console.error(`\u274C Missing required environment variables: ${missing.join(", ")}`); console.error(`\u{1F4A1} Please set the following in your .env file:`); missing.forEach((key) => { console.error(` ${key}=your_${key.toLowerCase()}`); }); throw new Error("Missing required OKX API credentials"); } console.error("\u2705 OKX API credentials found in environment"); } catch (error) { console.error("\u274C Invalid environment configuration:", error); throw error; } } function getEnvironmentConfig() { validateEnvironment(); return { apiKey: process.env.OKX_API_KEY, secretKey: process.env.OKX_SECRET_KEY, apiPassphrase: process.env.OKX_API_PASSPHRASE, projectId: process.env.OKX_PROJECT_ID }; } // src/index.ts var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js"); var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js"); // src/agent/analytics.ts var import_okx_dex_sdk = require("@okx-dex/okx-dex-sdk"); // src/types.ts var OKXDEXError = class extends Error { constructor(message, code, details) { super(message); this.name = "OKXDEXError"; this.code = code; this.details = details; } }; // src/agent/analytics.ts var OKXDEXAnalyticsAgent = class { constructor(environment) { this.environment = environment; this.client = new import_okx_dex_sdk.OKXDexClient({ apiKey: environment.apiKey, secretKey: environment.secretKey, apiPassphrase: environment.apiPassphrase, projectId: environment.projectId }); } /** * Get quote for token swap */ async getQuote(chainId, fromTokenAddress, toTokenAddress, amount, slippage = "0.005") { try { const response = await this.client.dex.getQuote({ chainId, fromTokenAddress, toTokenAddress, amount, slippage }); if (response.code !== "0" || !response.data || response.data.length === 0) { throw new OKXDEXError(`Quote request failed: ${response.msg}`, response.code); } const quoteData = response.data[0]; return { chainId: quoteData.chainId, fromToken: { address: quoteData.fromToken.tokenContractAddress, symbol: quoteData.fromToken.tokenSymbol, decimals: quoteData.fromToken.decimal, unitPrice: quoteData.fromToken.tokenUnitPrice }, toToken: { address: quoteData.toToken.tokenContractAddress, symbol: quoteData.toToken.tokenSymbol, decimals: quoteData.toToken.decimal, unitPrice: quoteData.toToken.tokenUnitPrice }, fromAmount: quoteData.fromTokenAmount, toAmount: quoteData.toTokenAmount, estimatedGas: quoteData.estimateGasFee, priceImpact: quoteData.priceImpactPercentage, dexRouters: quoteData.dexRouterList.map((router) => ({ router: router.router, percent: router.routerPercent, subRouters: router.subRouterList.map((sub) => ({ dexProtocol: sub.dexProtocol })) })), quoteCompare: quoteData.quoteCompareList || [], tradeFee: quoteData.tradeFee, timestamp: Date.now() }; } catch (error) { if (error instanceof OKXDEXError) { throw error; } throw new OKXDEXError(`Failed to get quote: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Get liquidity information for a chain */ async getLiquidity(chainId) { try { const response = await this.client.dex.getLiquidity(chainId); if (response.code !== "0" || !response.data) { throw new OKXDEXError(`Liquidity request failed: ${response.msg}`, response.code); } return { chainId, chainName: this.getChainName(chainId), liquiditySources: response.data }; } catch (error) { if (error instanceof OKXDEXError) { throw error; } throw new OKXDEXError(`Failed to get liquidity: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Get chain information */ async getChainInfo(chainId) { try { if (chainId) { const response = await this.client.dex.getChainData(chainId); if (response.code !== "0" || !response.data || response.data.length === 0) { throw new OKXDEXError(`Chain info request failed: ${response.msg}`, response.code); } return response.data[0]; } else { const chainInfos = []; const supportedChainIds = Object.values(SUPPORTED_CHAINS); for (const id of supportedChainIds.slice(0, 10)) { try { const info = await this.getChainInfo(id); chainInfos.push(info); await new Promise((resolve) => setTimeout(resolve, 100)); } catch (error) { console.warn(`Failed to get info for chain ${id}:`, error); } } return chainInfos; } } catch (error) { if (error instanceof OKXDEXError) { throw error; } throw new OKXDEXError(`Failed to get chain info: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Compare quotes across multiple chains for the same token pair */ async compareMultiChainQuotes(fromTokenAddress, toTokenAddress, amount, chainIds) { const results = []; for (const chainId of chainIds) { try { const quote = await this.getQuote( chainId, fromTokenAddress, toTokenAddress, amount ); results.push({ chainId, chainName: this.getChainName(chainId), quote }); await new Promise((resolve) => setTimeout(resolve, 200)); } catch (error) { results.push({ chainId, chainName: this.getChainName(chainId), error: error instanceof Error ? error.message : "Unknown error" }); } } return results; } /** * Get market summary with available chains */ async getMarketSummary() { try { const supportedChainsList = Object.entries(SUPPORTED_CHAINS).map(([name, id]) => ({ chainId: id, chainName: name.replace(/_/g, " ").toLowerCase().replace(/\b\w/g, (l) => l.toUpperCase()), isEVM: !["501", "784", "607", "195"].includes(id) // Non-EVM chains })); return { supportedChains: supportedChainsList.length, availableChains: supportedChainsList, timestamp: Date.now() }; } catch (error) { if (error instanceof OKXDEXError) { throw error; } throw new OKXDEXError(`Failed to get market summary: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Get tokens from quotes (since there's no direct token list endpoint) */ async getPopularTokenPairs(chainId) { try { const commonTokens = { "1": [ // Ethereum { address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", symbol: "ETH" }, { address: "0xA0b86a33E6417f84bDD9F42DD3a24b1A7FB5Ce15", symbol: "USDC" }, { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", symbol: "USDT" } ], "137": [ // Polygon { address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", symbol: "MATIC" }, { address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", symbol: "USDC" }, { address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", symbol: "USDT" } ], "8453": [ // Base { address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", symbol: "ETH" }, { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", symbol: "USDC" } ] }; const tokens = commonTokens[chainId] || commonTokens["1"]; const pairs = []; for (let i = 0; i < tokens.length - 1; i++) { try { const quote = await this.getQuote( chainId, tokens[i].address, tokens[i + 1].address, "1000000000000000000" // 1 token ); pairs.push({ fromToken: quote.fromToken, toToken: quote.toToken, priceImpact: quote.priceImpact }); await new Promise((resolve) => setTimeout(resolve, 200)); } catch (error) { console.warn(`Failed to get quote for ${tokens[i].symbol}/${tokens[i + 1].symbol}:`, error); } } return pairs; } catch (error) { throw new OKXDEXError(`Failed to get popular token pairs: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Helper method to get chain name from ID */ getChainName(chainId) { const chainNames = { "1": "Ethereum", "10": "Optimism", "56": "BSC", "66": "OKT Chain", "130": "Uni Chain", "146": "Sonic", "196": "X Layer", "137": "Polygon", "42161": "Arbitrum", "43114": "Avalanche", "324": "zkSync Era", "1101": "Polygon zkEVM", "8453": "Base", "59144": "Linea", "250": "Fantom", "5000": "Mantle", "1030": "Conflux", "1088": "Metis", "4200": "Merlin Chain", "81457": "Blast", "169": "Manta Pacific", "534352": "Scroll", "25": "Cronos", "7000": "Zeta Chain", "195": "Tron", "501": "Solana", "784": "SUI", "607": "TON" }; return chainNames[chainId] || `Chain ${chainId}`; } }; // src/mcp/quote/get_quote_tools.ts var GetQuoteTool = { name: "get_okx_quote", description: "Get swap quote from OKX DEX aggregator for token pairs across multiple chains", schema: { chainId: QuoteParamsSchema.shape.chainId, fromTokenAddress: QuoteParamsSchema.shape.fromTokenAddress, toTokenAddress: QuoteParamsSchema.shape.toTokenAddress, amount: QuoteParamsSchema.shape.amount, slippage: QuoteParamsSchema.shape.slippage }, handler: async (agent, params) => { const validatedParams = QuoteParamsSchema.parse(params); const result = await agent.getQuote( validatedParams.chainId, validatedParams.fromTokenAddress, validatedParams.toTokenAddress, validatedParams.amount, validatedParams.slippage ); return { status: "success", data: result }; } }; var CompareMultiChainQuotesTool = { name: "compare_multichain_quotes", description: "Compare quotes for the same token pair across multiple chains", schema: { fromTokenAddress: CompareQuotesParamsSchema.shape.fromTokenAddress, toTokenAddress: CompareQuotesParamsSchema.shape.toTokenAddress, amount: CompareQuotesParamsSchema.shape.amount, chainIds: CompareQuotesParamsSchema.shape.chainIds }, handler: async (agent, params) => { const validatedParams = CompareQuotesParamsSchema.parse(params); const result = await agent.compareMultiChainQuotes( validatedParams.fromTokenAddress, validatedParams.toTokenAddress, validatedParams.amount, validatedParams.chainIds ); return { status: "success", data: result }; } }; var GetPopularTokenPairsTool = { name: "get_okx_popular_token_pairs", description: "Get popular token pairs and their quotes for a specific chain", schema: { chainId: QuoteParamsSchema.shape.chainId }, handler: async (agent, params) => { const { chainId } = params; const result = await agent.getPopularTokenPairs(chainId); return { status: "success", data: result }; } }; // src/mcp/liquidity/get_liquidity_tools.ts var GetLiquidityTool = { name: "get_okx_liquidity", description: "Get liquidity sources for a specific chain from OKX DEX", schema: { chainId: LiquidityParamsSchema.shape.chainId }, handler: async (agent, params) => { const validatedParams = LiquidityParamsSchema.parse(params); const result = await agent.getLiquidity(validatedParams.chainId); return { status: "success", data: result }; } }; var GetMultiChainLiquidityTool = { name: "get_okx_multichain_liquidity", description: "Get liquidity information across multiple chains for comparison", schema: { chainIds: { type: "array", items: { type: "string" }, description: "List of chain IDs to get liquidity for", default: ["1", "137", "8453"] } }, handler: async (agent, params) => { const { chainIds = ["1", "137", "8453"] } = params; const results = []; for (const chainId of chainIds) { try { const liquidity = await agent.getLiquidity(chainId); results.push(liquidity); await new Promise((resolve) => setTimeout(resolve, 300)); } catch (error) { results.push({ chainId, chainName: `Chain ${chainId}`, error: error instanceof Error ? error.message : "Unknown error", liquiditySources: [] }); } } return { status: "success", data: { chains: results, summary: { totalChains: results.filter((r) => !r.error).length, timestamp: Date.now() } } }; } }; // src/mcp/chain/get_chain_tools.ts var GetSupportedChainsTool = { name: "get_okx_supported_chains", description: "Get all supported chains with their configuration details", schema: {}, handler: async (agent, params) => { const result = await agent.getChainInfo(); return { status: "success", data: result }; } }; var GetChainInfoTool = { name: "get_okx_chain_info", description: "Get detailed information for a specific chain", schema: { chainId: ChainInfoParamsSchema.shape.chainId }, handler: async (agent, params) => { const validatedParams = ChainInfoParamsSchema.required({ chainId: true }).parse(params); const result = await agent.getChainInfo(validatedParams.chainId); return { status: "success", data: result }; } }; // src/mcp/market/get_market_tools.ts var GetMarketSummaryTool = { name: "get_okx_market_summary", description: "Get comprehensive market summary across OKX DEX supported chains", schema: {}, handler: async (agent, params) => { const result = await agent.getMarketSummary(); return { status: "success", data: result }; } }; var GetDEXOverviewTool = { name: "get_okx_dex_overview", description: "Get overview of OKX DEX aggregator capabilities and supported features", schema: {}, handler: async (agent, params) => { const supportedChainsList = Object.entries(SUPPORTED_CHAINS).map(([name, id]) => ({ name: name.replace(/_/g, " ").toLowerCase().replace(/\b\w/g, (l) => l.toUpperCase()), chainId: id, isEVM: !["501", "784", "607", "195"].includes(id) // Non-EVM chains })); const result = { platform: "OKX DEX Aggregator", description: "Multi-chain DEX aggregator supporting both EVM and non-EVM chains", features: [ "Multi-chain support across 28+ networks", "Both EVM and non-EVM chain support", "DEX aggregation for optimal pricing", "Smart routing across multiple protocols", "Real-time liquidity analysis", "Cross-chain quote comparison", "Low slippage optimization" ], supportedChains: supportedChainsList, totalSupportedChains: supportedChainsList.length, evmChains: supportedChainsList.filter((c) => c.isEVM).length, nonEvmChains: supportedChainsList.filter((c) => !c.isEVM).length, keyMetrics: { chainsSupported: supportedChainsList.length, evmChains: supportedChainsList.filter((c) => c.isEVM).length, nonEvmChains: supportedChainsList.filter((c) => !c.isEVM).length, aggregatedDEXs: "100+", averageSlippage: "< 1%" }, apiCapabilities: [ "Token quotes and pricing", "Multi-protocol routing", "Liquidity source analysis", "Chain information", "Cross-chain comparisons" ], timestamp: Date.now() }; return { status: "success", data: result }; } }; // src/mcp/converter/symbol_converter_tool.ts var import_zod2 = require("zod"); var SymbolConverterInstructionsSchema = import_zod2.z.object({ symbol: import_zod2.z.string().describe("Token symbol to convert (e.g., USDC, WETH, UNI)"), chainId: import_zod2.z.string().describe("Chain ID to search on (e.g., '1' for Ethereum, '137' for Polygon)"), useNoditSearch: import_zod2.z.boolean().optional().default(true).describe("Whether to use Nodit search API for comprehensive results") }); var SymbolToAddressInstructionsTool = { name: "get_symbol_to_address", description: "Convert token symbols to contract addresses", schema: { symbol: SymbolConverterInstructionsSchema.shape.symbol, chainId: SymbolConverterInstructionsSchema.shape.chainId, useNoditSearch: SymbolConverterInstructionsSchema.shape.useNoditSearch }, handler: async (agent, params) => { const validatedParams = SymbolConverterInstructionsSchema.parse(params); const { symbol, chainId, useNoditSearch } = validatedParams; const chainIdToProtocol = { "1": "ethereum", "137": "polygon", "42161": "arbitrum", "8453": "base", "10": "optimism", "25": "ethereum", // Cronos not directly supported, fallback to ethereum "56": "ethereum", // BSC not directly supported, fallback to ethereum "43114": "ethereum" // Avalanche not directly supported }; const protocol = chainIdToProtocol[chainId] || "ethereum"; const network = "mainnet"; const instructions = { status: "instruction", message: `Converting symbol '${symbol}' to contract address on chain ${chainId}`, symbol: symbol.toUpperCase(), chainId, protocol, network, // Multi-step approach steps: [ { step: 1, method: "nodit_search", enabled: useNoditSearch && !!chainIdToProtocol[chainId], description: "Search using Nodit API for comprehensive token metadata", action: { tool: "nodit:call_nodit_api", parameters: { protocol, network, operationId: "searchTokenContractMetadataByKeyword", requestBody: { keyword: symbol.toUpperCase(), page: 1, rpp: 10, withCount: true } } }, expected_result: "List of token contracts with metadata including address, name, symbol, decimals", scoring_criteria: [ "Exact symbol match gets highest priority", "Verified/popular tokens preferred", "Higher total supply often indicates main token", "Consider deployment date (older = more established)" ] }, { step: 2, method: "okx_quote_discovery", description: "Use OKX quote API to discover token addresses through common pairs", enabled: true, action: { tool: "get_okx_popular_token_pairs", parameters: { chainId } }, expected_result: "Popular token pairs that may contain the target token", note: "Look for tokens in quote responses that match the symbol" }, { step: 3, method: "cached_lookup", description: "Check against known major token addresses", enabled: true, known_tokens: { ethereum: { USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", USDT: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", WETH: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", WBTC: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", DAI: "0x6b175474e89094c44da98b954eedeac495271d0f", UNI: "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", LINK: "0x514910771af9ca656af840dff83e8264ecf986ca" }, polygon: { USDC: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", WPOL: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270" }, base: { USDC: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", WETH: "0x4200000000000000000000000000000000000006" }, arbitrum: { USDC: "0xaf88d065e77c8cc2239327c5edb3a432268e5831", WETH: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1" } } } ], // Processing workflow workflow: { 1: "If cached token exists, return immediately with high confidence", 2: useNoditSearch && chainIdToProtocol[chainId] ? "Execute Nodit search and filter results by exact symbol match" : "Skip Nodit search (chain not supported or disabled)", 3: "Execute OKX popular pairs discovery", 4: "Combine and rank results by confidence score", 5: "Return top candidate with metadata" }, // Result format expected_output: { status: "success", symbol: symbol.toUpperCase(), chainId, results: [ { address: "0x...", symbol: symbol.toUpperCase(), name: "Token Name", decimals: 18, confidence: "high|medium|low", source: "cached|nodit|okx_discovery", verified: true, metadata: { totalSupply: "optional", deployedAt: "optional", logoUrl: "optional" } } ] }, // Quick result for known tokens quick_result: (() => { const knownTokens = { "1": { // Ethereum USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", USDT: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", WETH: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", WBTC: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", DAI: "0x6b175474e89094c44da98b954eedeac495271d0f" }, "137": { // Polygon USDC: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", WPOL: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270" }, "8453": { // Base USDC: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", WETH: "0x4200000000000000000000000000000000000006" } }; const chainTokens = knownTokens[chainId]; const knownAddress = chainTokens?.[symbol.toUpperCase()]; if (knownAddress) { return { address: knownAddress, symbol: symbol.toUpperCase(), name: `${symbol.toUpperCase()} Token`, decimals: symbol.toUpperCase() === "USDC" || symbol.toUpperCase() === "USDT" ? 6 : 18, confidence: "high", source: "cached_major_tokens", verified: true }; } return null; })(), // Error handling fallback_options: [ "Try searching with partial symbol matches", "Check if token exists on different chains", "Verify symbol spelling and try again", "Use block explorer to manually find contract address" ], timestamp: Date.now() }; return { status: "success", data: instructions }; } }; // src/mcp/index.ts var OKXDEXAnalyticsTools = { // === QUOTE & PRICING (3 tools) === "GetQuoteTool": GetQuoteTool, // Get swap quotes "CompareMultiChainQuotesTool": CompareMultiChainQuotesTool, // Cross-chain quote comparison "GetPopularTokenPairsTool": GetPopularTokenPairsTool, // Popular token pairs // === LIQUIDITY ANALYTICS (2 tools) === "GetLiquidityTool": GetLiquidityTool, // Chain liquidity info "GetMultiChainLiquidityTool": GetMultiChainLiquidityTool, // Multi-chain liquidity // === CHAIN INFORMATION (2 tools) === "GetSupportedChainsTool": GetSupportedChainsTool, // All supported chains "GetChainInfoTool": GetChainInfoTool, // Specific chain info // === MARKET & OVERVIEW (2 tools) === "GetMarketSummaryTool": GetMarketSummaryTool, // Market overview "GetDEXOverviewTool": GetDEXOverviewTool, // OKX DEX overview // === SYMBOL CONVERTER (1 tool) === "SymbolToAddressInstructionsTool": SymbolToAddressInstructionsTool // Symbol to address conversion instructions }; var TOOL_COUNT = Object.keys(OKXDEXAnalyticsTools).length; // src/index.ts function createOKXDEXMcpServer(agent) { const server = new import_mcp.McpServer({ name: "okx-dex-mcp", version: "0.1.0" }); for (const [toolKey, tool] of Object.entries(OKXDEXAnalyticsTools)) { server.tool(tool.name, tool.description, tool.schema, async (params) => { try { const result = await tool.handler(agent, params); return { content: [ { type: "text", text: JSON.stringify(result, null, 2) } ] }; } catch (error) { console.error(`Tool execution error [${tool.name}]:`, error); return { isError: true, content: [ { type: "text", text: error instanceof Error ? error.message : "Unknown error occurred" } ] }; } }); } console.error(`\u2705 Registered ${TOOL_COUNT} OKX DEX analytics tools`); return server; } async function main() { try { console.error("\u{1F50D} Starting OKX DEX Analytics MCP Server..."); validateEnvironment(); const environment = getEnvironmentConfig(); const okxAgent = new OKXDEXAnalyticsAgent(environment); const server = createOKXDEXMcpServer(okxAgent); const transport = new import_stdio.StdioServerTransport(); await server.connect(transport); console.error("\u2705 OKX DEX Analytics MCP Server is running!"); console.error("\u{1F527} Available capabilities:"); console.error(" \u2022 Multi-chain DEX aggregator analytics"); console.error(" \u2022 Token quotes & pricing across 20+ chains"); console.error(" \u2022 Liquidity analysis & protocol comparison"); console.error(" \u2022 Cross-chain quote optimization"); console.error(" \u2022 Token discovery & market data"); console.error(" \u2022 DEX protocol analytics"); console.error(" \u2022 Chain statistics & comparisons"); console.error(" \u2022 Market overview & insights"); console.error("\u{1F310} Supported chains:"); console.error(" \u2022 Ethereum, Polygon, Arbitrum, Base, Optimism"); console.error(" \u2022 Avalanche, BSC, Cronos, Fantom, zkSync Era"); console.error(" \u2022 And 15+ more EVM-compatible chains"); } catch (error) { console.error("\u274C Error starting OKX DEX Analytics MCP server:", error); process.exit(1); } } main();