@tamago-labs/cronos-mcp
Version:
MCP server for Cronos Web3 Analytics - comprehensive blockchain analytics for Cronos and zkEVM networks with VVS Finance DEX integration
1 lines • 112 kB
Source Map (JSON)
{"version":3,"sources":["../src/config.ts","../src/index.ts","../src/agent/analytics.ts","../src/mcp/balance/get_balance_tools.ts","../src/mcp/transaction/get_transaction_tools.ts","../src/mcp/block/get_block_tools.ts","../src/mcp/defi/get_defi_tools.ts","../src/mcp/cronosid/get_cronosid_tools.ts","../src/mcp/vvs/get_vvs_tools.ts","../src/mcp/vvs/vvs_analytics.ts","../src/types.ts","../src/mcp/market/get_market_tools.ts","../src/mcp/index.ts"],"sourcesContent":["import { CronosEvm, CronosZkEvm, Client } from '@crypto.com/developer-platform-client';\n\nexport type CronosNetwork = 'cronos-mainnet' | 'cronos-zkevm-mainnet';\n\nexport interface NetworkConfig {\n name: string;\n chain: CronosEvm | CronosZkEvm;\n rpcUrl: string;\n blockExplorer: string;\n nativeCurrency: string;\n apiKeyRequired: boolean;\n}\n\nexport const NETWORK_CONFIGS: Record<CronosNetwork, NetworkConfig> = {\n 'cronos-mainnet': {\n name: 'Cronos Mainnet',\n chain: CronosEvm.Mainnet,\n rpcUrl: 'https://evm.cronos.org',\n blockExplorer: 'https://cronoscan.com',\n nativeCurrency: 'CRO',\n apiKeyRequired: true\n },\n 'cronos-zkevm-mainnet': {\n name: 'Cronos zkEVM Mainnet', \n chain: CronosZkEvm.Mainnet,\n rpcUrl: 'https://mainnet.zkevm.cronos.org',\n blockExplorer: 'https://explorer.zkevm.cronos.org',\n nativeCurrency: 'zkCRO',\n apiKeyRequired: true\n }\n};\n\nconst getArgs = () =>\n process.argv.reduce((args: any, arg: any) => {\n // long arg\n if (arg.slice(0, 2) === \"--\") {\n const longArg = arg.split(\"=\");\n const longArgFlag = longArg[0].slice(2);\n const longArgValue = longArg.length > 1 ? longArg[1] : true;\n args[longArgFlag] = longArgValue;\n }\n // flags\n else if (arg[0] === \"-\") {\n const flags = arg.slice(1).split(\"\");\n flags.forEach((flag: any) => {\n args[flag] = true;\n });\n }\n return args;\n }, {});\n\nconst getApiKeys = (): { evmKey: string; zkevmKey: string } => {\n const args = getArgs();\n \n return {\n evmKey: process.env.CRONOS_EVM_API_KEY || args.cronos_evm_api_key || '',\n zkevmKey: process.env.CRONOS_ZKEVM_API_KEY || args.cronos_zkevm_api_key || ''\n };\n};\n\nconst determineNetwork = (): CronosNetwork => {\n const args = getArgs();\n const { evmKey, zkevmKey } = getApiKeys();\n \n // If network is explicitly specified, use it (for override)\n if (args.network) {\n const network = args.network as CronosNetwork;\n if (network in NETWORK_CONFIGS) {\n return network;\n }\n throw new Error(`Invalid network: ${network}. Must be one of: ${Object.keys(NETWORK_CONFIGS).join(', ')}`);\n }\n \n // Auto-detect network based on available API keys\n if (evmKey && zkevmKey) {\n // Both keys available, default to Cronos Mainnet\n console.error('🔑 Both API keys detected, defaulting to Cronos Mainnet');\n console.error('💡 Use --network=cronos-zkevm-mainnet to use zkEVM instead');\n return 'cronos-mainnet';\n } else if (evmKey) {\n console.error('🔑 Cronos EVM API key detected, using Cronos Mainnet');\n return 'cronos-mainnet';\n } else if (zkevmKey) {\n console.error('🔑 Cronos zkEVM API key detected, using zkEVM Mainnet');\n return 'cronos-zkevm-mainnet';\n } else {\n throw new Error('❌ No API keys provided! Set CRONOS_EVM_API_KEY or CRONOS_ZKEVM_API_KEY');\n }\n};\n\nconst getApiKeyForNetwork = (network: CronosNetwork): string => {\n const { evmKey, zkevmKey } = getApiKeys();\n return network === 'cronos-mainnet' ? evmKey : zkevmKey;\n};\n\n// Initialize configuration\nexport const network = determineNetwork();\nexport const networkInfo = NETWORK_CONFIGS[network];\nexport const apiKey = getApiKeyForNetwork(network);\n\n// Initialize Cronos Client\nif (apiKey) {\n Client.init({\n chain: networkInfo.chain,\n apiKey: apiKey\n } as any);\n} else {\n console.warn(`⚠️ No API key provided for ${network}. Some features may be limited.`);\n}\n\nexport function validateEnvironment(): void {\n try {\n const { evmKey, zkevmKey } = getApiKeys();\n const keyType = network === 'cronos-mainnet' ? 'CRONOS_EVM_API_KEY' : 'CRONOS_ZKEVM_API_KEY';\n \n console.error(`✅ Cronos MCP environment configuration valid`);\n console.error(`📍 Active Network: ${networkInfo.name}`);\n console.error(`📍 RPC URL: ${networkInfo.rpcUrl}`);\n console.error(`📍 Block Explorer: ${networkInfo.blockExplorer}`);\n console.error(`📍 Native Currency: ${networkInfo.nativeCurrency}`);\n \n if (apiKey) {\n console.error(`📍 Active API Key (${keyType}): ****${apiKey.slice(-4)}`);\n }\n \n // Show availability of both networks\n console.error(`🌐 Network Availability:`);\n console.error(` • Cronos Mainnet: ${evmKey ? '✅ Available' : '❌ Missing CRONOS_EVM_API_KEY'}`);\n console.error(` • zkEVM Mainnet: ${zkevmKey ? '✅ Available' : '❌ Missing CRONOS_ZKEVM_API_KEY'}`);\n \n } catch (error) {\n console.error('❌ Invalid environment configuration:', error);\n throw error;\n }\n}\n\n// Helper function to create client for different network\nexport function initializeClientForNetwork(targetNetwork: CronosNetwork): void {\n const config = NETWORK_CONFIGS[targetNetwork];\n const key = getApiKeyForNetwork(targetNetwork);\n \n if (key) {\n Client.init({\n chain: config.chain,\n apiKey: key\n } as any);\n console.error(`🔄 Switched to ${config.name} with API key ****${key.slice(-4)}`);\n } else {\n const requiredKey = targetNetwork === 'cronos-mainnet' ? 'CRONOS_EVM_API_KEY' : 'CRONOS_ZKEVM_API_KEY';\n throw new Error(`Missing ${requiredKey} for ${config.name}`);\n }\n}\n\n// Helper to get all available API keys for validation\nexport function getAllApiKeys(): Record<CronosNetwork, string> {\n const { evmKey, zkevmKey } = getApiKeys();\n return {\n 'cronos-mainnet': evmKey,\n 'cronos-zkevm-mainnet': zkevmKey\n };\n}\n\n// Helper to check if network switching is possible\nexport function canSwitchToNetwork(targetNetwork: CronosNetwork): boolean {\n return !!getApiKeyForNetwork(targetNetwork);\n}\n\n// Helper to get available networks\nexport function getAvailableNetworks(): CronosNetwork[] {\n const { evmKey, zkevmKey } = getApiKeys();\n const available: CronosNetwork[] = [];\n \n if (evmKey) available.push('cronos-mainnet');\n if (zkevmKey) available.push('cronos-zkevm-mainnet');\n \n return available;\n}\n","#!/usr/bin/env node\n\nimport { validateEnvironment, getAllApiKeys, getAvailableNetworks } from \"./config\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CronosAnalyticsAgent } from \"./agent/analytics\";\nimport { CronosAnalyticsTools, TOOL_COUNT } from \"./mcp\";\n\n/**\n * Creates an MCP server for Cronos Web3 Analytics\n * Supports both Cronos Mainnet and zkEVM Mainnet\n * Provides comprehensive analytics without requiring private keys\n */\nfunction createCronosMcpServer(agent: CronosAnalyticsAgent) {\n // Create MCP server instance\n const server = new McpServer({\n name: \"cronos-mcp\",\n version: \"0.1.0\"\n });\n\n // Register all analytics tools\n for (const [toolKey, tool] of Object.entries(CronosAnalyticsTools)) {\n server.tool(tool.name, tool.description, tool.schema, async (params: any): Promise<any> => {\n try {\n // Execute the handler with the agent and params\n const result = await tool.handler(agent, params);\n\n // Format the result as MCP tool response\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n console.error(`Tool execution error [${tool.name}]:`, error);\n // Handle errors in MCP format\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: error instanceof Error\n ? error.message\n : \"Unknown error occurred\",\n },\n ],\n };\n }\n });\n }\n\n console.error(`✅ Registered ${TOOL_COUNT} Cronos analytics tools`);\n return server;\n}\n\nasync function main() {\n try {\n console.error(\"🔍 Starting Cronos Analytics MCP Server...\");\n\n // Validate environment before proceeding\n validateEnvironment();\n\n // Check available networks\n const availableNetworks = getAvailableNetworks();\n \n if (availableNetworks.length === 0) {\n console.error('❌ No API keys found for any network!');\n console.error('💡 Please set CRONOS_EVM_API_KEY and/or CRONOS_ZKEVM_API_KEY');\n process.exit(1);\n }\n\n // Create Cronos Analytics agent \n const cronosAgent = new CronosAnalyticsAgent();\n\n // Create and start MCP server\n const server = createCronosMcpServer(cronosAgent);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"✅ Cronos Analytics MCP Server is running!\");\n console.error(\"🔧 Available capabilities:\");\n console.error(\" • Native token balance analytics (CRO/zkCRO)\");\n console.error(\" • ERC20 token analytics with VVS pricing\");\n console.error(\" • Advanced wallet analytics & batch queries\");\n console.error(\" • Transaction & block analysis\");\n console.error(\" • DeFi protocols (VVS Finance, H2 Finance)\");\n console.error(\" • VVS Finance DEX integration (top DEX on Cronos)\");\n console.error(\" • CronosId domain resolution (.cro)\");\n console.error(\" • Exchange market data\");\n console.error(` • ${availableNetworks.length === 2 ? 'Multi-network' : 'Single-network'} support`);\n \n if (availableNetworks.length === 2) {\n console.error(\"🌐 Cross-network analytics enabled!\");\n } else {\n const hasEvm = availableNetworks.includes('cronos-mainnet');\n const missingNetwork = hasEvm ? 'zkEVM Mainnet' : 'Cronos Mainnet';\n const missingKey = hasEvm ? 'CRONOS_ZKEVM_API_KEY' : 'CRONOS_EVM_API_KEY';\n console.error(`💡 Add ${missingKey} to enable ${missingNetwork} analytics`);\n }\n\n } catch (error) {\n console.error('❌ Error starting Cronos Analytics MCP server:', error);\n process.exit(1);\n }\n}\n\nmain();\n","import { Token, Wallet, Block, Transaction, Exchange, Defi, CronosId } from \"@crypto.com/developer-platform-client\";\nimport { DefiProtocol } from \"@crypto.com/developer-platform-client\";\nimport { networkInfo, apiKey, initializeClientForNetwork, canSwitchToNetwork, type CronosNetwork } from '../config';\nimport type { AnalyticsResponse, TokenBalance, TransactionData, NetworkStats } from '../types';\n\nexport class CronosAnalyticsAgent {\n public network: CronosNetwork;\n public networkInfo: typeof networkInfo;\n\n constructor(targetNetwork?: CronosNetwork) {\n this.network = targetNetwork || 'cronos-mainnet';\n this.networkInfo = networkInfo;\n\n // Initialize client for target network if different\n if (targetNetwork && targetNetwork !== this.network) {\n if (canSwitchToNetwork(targetNetwork)) {\n initializeClientForNetwork(targetNetwork);\n this.network = targetNetwork;\n } else {\n const requiredKey = targetNetwork === 'cronos-mainnet' ? 'CRONOS_EVM_API_KEY' : 'CRONOS_ZKEVM_API_KEY';\n console.warn(`⚠️ Cannot switch to ${targetNetwork}: Missing ${requiredKey}`);\n console.warn(`📍 Continuing with default network: ${this.network}`);\n }\n }\n\n console.error(`🔍 Cronos Analytics Agent initialized for ${this.network}`);\n console.error(`📍 Network: ${this.networkInfo.name}`);\n console.error(`🔗 Block Explorer: ${this.networkInfo.blockExplorer}`);\n }\n\n // === BALANCE & TOKEN ANALYTICS ===\n\n async getNativeTokenBalance(address: string): Promise<AnalyticsResponse> {\n try {\n const result = await Token.getNativeTokenBalance(address);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n currency: this.networkInfo.nativeCurrency\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get native balance',\n network: this.network\n };\n }\n }\n\n async getERC20TokenBalance(address: string, contractAddress: string): Promise<AnalyticsResponse> {\n try {\n const result = await Token.getERC20TokenBalance(address, contractAddress);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n contractAddress\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get ERC20 balance',\n network: this.network\n };\n }\n }\n\n async getWalletOverview(address: string): Promise<AnalyticsResponse> {\n try {\n const result = await Wallet.balance(address);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n address\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get wallet overview',\n network: this.network\n };\n }\n }\n\n // === TRANSACTION ANALYTICS ===\n\n async getTransactionByHash(transactionHash: string): Promise<AnalyticsResponse> {\n try {\n const result = await Transaction.getTransactionByHash(transactionHash);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n blockExplorer: `${this.networkInfo.blockExplorer}/tx/${transactionHash}`\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get transaction details',\n network: this.network\n };\n }\n }\n\n async getTransactionStatus(transactionHash: string): Promise<AnalyticsResponse> {\n try {\n const result = await Transaction.getTransactionStatus(transactionHash);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n transactionHash\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get transaction status',\n network: this.network\n };\n }\n }\n\n // === BLOCK ANALYTICS ===\n\n async getCurrentBlock(): Promise<AnalyticsResponse> {\n try {\n const result = await Block.getCurrentBlock();\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get current block',\n network: this.network\n };\n }\n }\n\n async getBlockByTag(blockTag: string, includeTransactions: boolean = false): Promise<AnalyticsResponse> {\n try {\n const result = await Block.getBlockByTag(blockTag, includeTransactions ? 'true' : 'false');\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n blockTag,\n includeTransactions\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get block data',\n network: this.network\n };\n }\n }\n\n // === DEFI ANALYTICS ===\n\n async getWhitelistedTokens(protocol: DefiProtocol): Promise<AnalyticsResponse> {\n try {\n const result = await Defi.getWhitelistedTokens(protocol);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n protocol\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get whitelisted tokens',\n network: this.network\n };\n }\n }\n\n async getAllFarms(protocol: DefiProtocol): Promise<AnalyticsResponse> {\n try {\n const result = await Defi.getAllFarms(protocol);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n protocol\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get farms',\n network: this.network\n };\n }\n }\n\n async getFarmBySymbol(protocol: DefiProtocol, symbol: string): Promise<AnalyticsResponse> {\n try {\n const result = await Defi.getFarmBySymbol(protocol, symbol);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n protocol,\n symbol\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get farm data',\n network: this.network\n };\n }\n }\n\n // === CRONOS ID ANALYTICS ===\n\n async resolveCronosId(cronosId: string): Promise<AnalyticsResponse> {\n try {\n const result = await CronosId.forwardResolve(cronosId);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n cronosId\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to resolve CronosId',\n network: this.network\n };\n }\n }\n\n async reverseResolveCronosId(address: string): Promise<AnalyticsResponse> {\n try {\n const result = await CronosId.reverseResolve(address);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n address\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to reverse resolve address',\n network: this.network\n };\n }\n }\n\n // === EXCHANGE & MARKET ANALYTICS ===\n\n async getAllTickers(): Promise<AnalyticsResponse> {\n try {\n const result = await Exchange.getAllTickers();\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n exchangeInfo: 'Crypto.com Exchange data'\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get exchange tickers',\n network: this.network\n };\n }\n }\n\n async getTickerByInstrument(instrument: string): Promise<AnalyticsResponse> {\n try {\n const result = await Exchange.getTickerByInstrument(instrument);\n return {\n status: 'success',\n data: {\n ...result.data,\n network: this.network,\n instrument\n },\n network: this.network,\n timestamp: Date.now()\n };\n } catch (error: any) {\n return {\n status: 'error',\n message: error.message || 'Failed to get ticker data',\n network: this.network\n };\n }\n }\n\n // === UTILITY METHODS ===\n\n getNetworkInfo(): AnalyticsResponse {\n return {\n status: 'success',\n data: {\n network: this.network,\n name: this.networkInfo.name,\n rpcUrl: this.networkInfo.rpcUrl,\n blockExplorer: this.networkInfo.blockExplorer,\n nativeCurrency: this.networkInfo.nativeCurrency,\n hasApiKey: !!apiKey,\n canSwitchNetworks: {\n 'cronos-mainnet': canSwitchToNetwork('cronos-mainnet'),\n 'cronos-zkevm-mainnet': canSwitchToNetwork('cronos-zkevm-mainnet')\n }\n },\n network: this.network,\n timestamp: Date.now()\n };\n }\n\n // Format large numbers for better readability\n formatBalance(balance: string, decimals: number = 18): string {\n const balanceNum = parseFloat(balance) / Math.pow(10, decimals);\n if (balanceNum >= 1e9) return `${(balanceNum / 1e9).toFixed(2)}B`;\n if (balanceNum >= 1e6) return `${(balanceNum / 1e6).toFixed(2)}M`;\n if (balanceNum >= 1e3) return `${(balanceNum / 1e3).toFixed(2)}K`;\n return balanceNum.toFixed(4);\n }\n\n // Validate Ethereum address format\n isValidAddress(address: string): boolean {\n return /^0x[a-fA-F0-9]{40}$/.test(address);\n }\n\n // Validate transaction hash format\n isValidTxHash(hash: string): boolean {\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n }\n\n // Validate CronosId format\n isValidCronosId(name: string): boolean {\n return CronosId.isCronosId(name);\n }\n}\n","import { z } from \"zod\";\nimport { CronosAnalyticsAgent } from \"../../agent/analytics\";\nimport { type McpTool } from \"../../types\";\n\nexport const GetNativeBalanceTool: McpTool = {\n name: \"cronos_get_native_balance\",\n description: \"Get native token balance (CRO/zkCRO) for any address on Cronos networks\",\n schema: {\n address: z.string()\n .describe(\"Wallet address to check balance for (0x...)\")\n .regex(/^0x[a-fA-F0-9]{40}$/, \"Must be a valid Ethereum address\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = await targetAgent.getNativeTokenBalance(input.address);\n \n return {\n status: \"success\",\n message: `✅ Native balance retrieved for ${input.address}`,\n data: {\n address: input.address,\n ...result.data,\n blockExplorer: `${targetAgent.networkInfo.blockExplorer}/address/${input.address}`\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get native balance: ${error.message}`);\n }\n }\n};\n\nexport const GetERC20BalanceTool: McpTool = {\n name: \"cronos_get_erc20_balance\",\n description: \"Get ERC20 token balance for any address and token contract on Cronos networks\",\n schema: {\n address: z.string()\n .describe(\"Wallet address to check balance for (0x...)\")\n .regex(/^0x[a-fA-F0-9]{40}$/, \"Must be a valid Ethereum address\"),\n contractAddress: z.string()\n .describe(\"ERC20 token contract address (0x...)\")\n .regex(/^0x[a-fA-F0-9]{40}$/, \"Must be a valid contract address\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = await targetAgent.getERC20TokenBalance(input.address, input.contractAddress);\n \n return {\n status: \"success\",\n message: `✅ ERC20 balance retrieved for ${input.address}`,\n data: {\n address: input.address,\n contractAddress: input.contractAddress,\n ...result.data,\n blockExplorer: `${targetAgent.networkInfo.blockExplorer}/token/${input.contractAddress}?a=${input.address}`\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get ERC20 balance: ${error.message}`);\n }\n }\n};\n\nexport const GetWalletOverviewTool: McpTool = {\n name: \"cronos_get_wallet_overview\",\n description: \"Get comprehensive wallet overview including all token balances and portfolio summary\",\n schema: {\n address: z.string()\n .describe(\"Wallet address to analyze (0x...)\")\n .regex(/^0x[a-fA-F0-9]{40}$/, \"Must be a valid Ethereum address\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = await targetAgent.getWalletOverview(input.address);\n \n return {\n status: \"success\",\n message: `✅ Wallet overview retrieved for ${input.address}`,\n data: {\n address: input.address,\n ...result.data,\n blockExplorer: `${targetAgent.networkInfo.blockExplorer}/address/${input.address}`,\n analytics: {\n totalTokens: result.data?.tokens?.length || 0,\n hasActivity: result.data?.transactionCount > 0\n }\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get wallet overview: ${error.message}`);\n }\n }\n};\n\nexport const GetNetworkInfoTool: McpTool = {\n name: \"cronos_get_network_info\",\n description: \"Get current network information and statistics\",\n schema: {\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = targetAgent.getNetworkInfo();\n \n return {\n status: \"success\",\n message: `✅ Network information retrieved`,\n data: {\n ...result.data,\n capabilities: [\n \"Native token balance queries\",\n \"ERC20 token analytics\", \n \"Transaction analysis\",\n \"Contract information\",\n \"Exchange data integration\"\n ]\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get network info: ${error.message}`);\n }\n }\n};\n","import { z } from \"zod\";\nimport { CronosAnalyticsAgent } from \"../../agent/analytics\";\nimport { type McpTool } from \"../../types\";\n\nexport const GetTransactionTool: McpTool = {\n name: \"cronos_get_transaction\",\n description: \"Get detailed transaction information by transaction hash\",\n schema: {\n transactionHash: z.string()\n .describe(\"Transaction hash to look up (0x...)\")\n .regex(/^0x[a-fA-F0-9]{64}$/, \"Must be a valid transaction hash\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = await targetAgent.getTransactionByHash(input.transactionHash);\n \n return {\n status: \"success\",\n message: `✅ Transaction details retrieved`,\n data: {\n transactionHash: input.transactionHash,\n ...result.data,\n blockExplorer: `${targetAgent.networkInfo.blockExplorer}/tx/${input.transactionHash}`\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get transaction: ${error.message}`);\n }\n }\n};\n\nexport const GetTransactionStatusTool: McpTool = {\n name: \"cronos_get_transaction_status\",\n description: \"Get transaction confirmation status and details\",\n schema: {\n transactionHash: z.string()\n .describe(\"Transaction hash to check status (0x...)\")\n .regex(/^0x[a-fA-F0-9]{64}$/, \"Must be a valid transaction hash\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = await targetAgent.getTransactionStatus(input.transactionHash);\n \n return {\n status: \"success\",\n message: `✅ Transaction status retrieved`,\n data: {\n transactionHash: input.transactionHash,\n ...result.data,\n blockExplorer: `${targetAgent.networkInfo.blockExplorer}/tx/${input.transactionHash}`,\n statusSummary: {\n isConfirmed: result.data?.status === 'confirmed',\n isPending: result.data?.status === 'pending',\n hasFailed: result.data?.status === 'failed'\n }\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get transaction status: ${error.message}`);\n }\n }\n};\n","import { z } from \"zod\";\nimport { CronosAnalyticsAgent } from \"../../agent/analytics\";\nimport { type McpTool } from \"../../types\";\n\nexport const GetCurrentBlockTool: McpTool = {\n name: \"cronos_get_current_block\",\n description: \"Get the current latest block number and information\",\n schema: {\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = await targetAgent.getCurrentBlock();\n \n return {\n status: \"success\",\n message: `✅ Current block information retrieved`,\n data: {\n ...result.data,\n blockExplorer: `${targetAgent.networkInfo.blockExplorer}/block/${result.data?.blockNumber || 'latest'}`\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get current block: ${error.message}`);\n }\n }\n};\n\nexport const GetBlockByTagTool: McpTool = {\n name: \"cronos_get_block_by_tag\",\n description: \"Get block information by block number, 'latest', or 'pending'\",\n schema: {\n blockTag: z.string()\n .describe(\"Block identifier ('latest', 'pending', or block number in hex like '0x1b4')\"),\n includeTransactions: z.boolean()\n .optional()\n .describe(\"Include full transaction details (default: false)\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = await targetAgent.getBlockByTag(input.blockTag, input.includeTransactions || false);\n \n return {\n status: \"success\",\n message: `✅ Block data retrieved for ${input.blockTag}`,\n data: {\n ...result.data,\n blockExplorer: `${targetAgent.networkInfo.blockExplorer}/block/${input.blockTag}`\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get block data: ${error.message}`);\n }\n }\n};\n","import { z } from \"zod\";\nimport { DefiProtocol } from \"@crypto.com/developer-platform-client\";\nimport { CronosAnalyticsAgent } from \"../../agent/analytics\";\nimport { type McpTool } from \"../../types\";\n\nexport const GetAllFarmsTool: McpTool = {\n name: \"cronos_get_all_farms\",\n description: \"Get all yield farms for DeFi protocols with APR/APY data\",\n schema: {\n protocol: z.enum(['h2finance', 'vvsfinance'])\n .describe(\"DeFi protocol to query\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const protocol = input.protocol === 'h2finance' ? DefiProtocol.H2 : DefiProtocol.VVS;\n const result = await targetAgent.getAllFarms(protocol);\n \n const farms = Array.isArray(result.data) ? result.data : [];\n const activeFarms = farms.filter((farm: any) => !farm.isFinished);\n \n return {\n status: \"success\",\n message: `✅ Farm data retrieved for ${input.protocol}`,\n data: {\n ...result.data,\n protocol: input.protocol,\n summary: {\n totalFarms: farms.length,\n activeFarms: activeFarms.length,\n finishedFarms: farms.length - activeFarms.length,\n averageAPR: activeFarms.length > 0 \n ? activeFarms.reduce((sum: number, farm: any) => sum + (farm.baseApr || 0), 0) / activeFarms.length\n : 0\n }\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get farms: ${error.message}`);\n }\n }\n};\n\nexport const GetFarmBySymbolTool: McpTool = {\n name: \"cronos_get_farm_by_symbol\",\n description: \"Get specific farm details by LP symbol\",\n schema: {\n protocol: z.enum(['h2finance', 'vvsfinance'])\n .describe(\"DeFi protocol to query\"),\n symbol: z.string()\n .describe(\"LP symbol to query (e.g., 'zkCRO-MOON', 'CRO-USDC')\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const protocol = input.protocol === 'h2finance' ? DefiProtocol.H2 : DefiProtocol.VVS;\n const result = await targetAgent.getFarmBySymbol(protocol, input.symbol);\n \n return {\n status: \"success\",\n message: `✅ Farm details retrieved for ${input.symbol}`,\n data: {\n ...result.data,\n protocol: input.protocol,\n symbol: input.symbol,\n yieldInfo: {\n baseAPR: result.data?.baseApr || 0,\n baseAPY: result.data?.baseApy || 0,\n lpAPR: result.data?.lpApr || 0,\n lpAPY: result.data?.lpApy || 0,\n isActive: !result.data?.isFinished,\n rewardEndDate: result.data?.rewardEndAt\n }\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get farm data: ${error.message}`);\n }\n }\n};\n","import { z } from \"zod\";\nimport { CronosAnalyticsAgent } from \"../../agent/analytics\";\nimport { type McpTool } from \"../../types\";\n\nexport const ResolveCronosIdTool: McpTool = {\n name: \"cronos_resolve_cronosid\",\n description: \"Resolve a CronosId (like alice.cro) to its wallet address\",\n schema: {\n cronosId: z.string()\n .describe(\"CronosId to resolve (e.g., 'alice.cro')\")\n .regex(/^[a-zA-Z0-9_-]+\\.cro$/, \"Must be a valid CronosId ending with .cro\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n if (!targetAgent.isValidCronosId(input.cronosId)) {\n throw new Error(\"Invalid CronosId format. Must end with .cro\");\n }\n\n const result = await targetAgent.resolveCronosId(input.cronosId);\n \n return {\n status: \"success\",\n message: `✅ CronosId resolved: ${input.cronosId}`,\n data: {\n cronosId: input.cronosId,\n ...result.data,\n blockExplorer: result.data?.address \n ? `${targetAgent.networkInfo.blockExplorer}/address/${result.data.address}`\n : undefined\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to resolve CronosId: ${error.message}`);\n }\n }\n};\n\nexport const ReverseResolveCronosIdTool: McpTool = {\n name: \"cronos_reverse_resolve_cronosid\",\n description: \"Look up the CronosId associated with a wallet address\",\n schema: {\n address: z.string()\n .describe(\"Wallet address to reverse resolve (0x...)\")\n .regex(/^0x[a-fA-F0-9]{40}$/, \"Must be a valid Ethereum address\"),\n network: z.enum(['cronos-mainnet', 'cronos-zkevm-mainnet'])\n .optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const result = await targetAgent.reverseResolveCronosId(input.address);\n \n return {\n status: \"success\",\n message: `✅ Address reverse resolved: ${input.address}`,\n data: {\n address: input.address,\n ...result.data,\n hasCronosId: !!result.data?.name,\n blockExplorer: `${targetAgent.networkInfo.blockExplorer}/address/${input.address}`\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to reverse resolve address: ${error.message}`);\n }\n }\n};\n","import { z } from \"zod\";\nimport { CronosAnalyticsAgent } from \"../../agent/analytics\";\nimport { VVSAnalytics } from \"./vvs_analytics\";\nimport { type McpTool, AddressSchema, NetworkSchema } from \"../../types\";\n\nexport const GetVVSSupplyTool: McpTool = {\n name: \"cronos_get_vvs_supply\",\n description: \"Get VVS token supply information (total, circulating, burned)\",\n schema: {\n network: NetworkSchema.optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const vvsAnalytics = new VVSAnalytics(targetAgent);\n const result = await vvsAnalytics.getSupplyInfo();\n \n return {\n status: \"success\",\n message: `✅ VVS supply information retrieved`,\n data: {\n ...result.data,\n protocol: \"VVS Finance\",\n dex: \"Leading DEX on Cronos\"\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get VVS supply info: ${error.message}`);\n }\n }\n};\n\nexport const GetVVSSummaryTool: McpTool = {\n name: \"cronos_get_vvs_summary\",\n description: \"Get VVS DEX summary with top trading pairs and liquidity data\",\n schema: {\n limit: z.number()\n .optional()\n .describe(\"Limit number of pairs returned (default: 50, max: 1000)\") \n .default(50),\n network: NetworkSchema.optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const vvsAnalytics = new VVSAnalytics(targetAgent);\n const result = await vvsAnalytics.getSummary(input.limit || 50);\n \n return {\n status: \"success\",\n message: `✅ VVS DEX summary retrieved`,\n data: {\n ...result.data,\n protocol: \"VVS Finance\",\n description: \"Top DEX on Cronos network\",\n wcroInfo: {\n address: \"0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23\",\n note: \"CRO is represented as WCRO in pairs\"\n }\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get VVS summary: ${error.message}`);\n }\n }\n};\n\nexport const GetVVSTokensTool: McpTool = {\n name: \"cronos_get_vvs_tokens\",\n description: \"Get all tokens available on VVS with price information\",\n schema: {\n limit: z.number()\n .optional()\n .describe(\"Limit number of tokens returned (default: 100, max: 1000)\") \n .default(100),\n network: NetworkSchema.optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.network && input.network !== agent.network \n ? new CronosAnalyticsAgent(input.network)\n : agent;\n\n const vvsAnalytics = new VVSAnalytics(targetAgent);\n const result = await vvsAnalytics.getTokens(input.limit || 100);\n \n return {\n status: \"success\",\n message: `✅ VVS tokens retrieved`,\n data: {\n ...result.data,\n protocol: \"VVS Finance\",\n wcroInfo: {\n address: \"0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23\",\n note: \"Canonical WCRO address used by VVS\"\n }\n }\n };\n } catch (error: any) {\n throw new Error(`Failed to get VVS tokens: ${error.message}`);\n }\n }\n};\n\nexport const GetVVSTokenInfoTool: McpTool = {\n name: \"cronos_get_vvs_token_info\",\n description: \"Get specific token information from VVS including USD and CRO prices\",\n schema: {\n tokenAddress: AddressSchema\n .describe(\"Token contract address (0x...)\"),\n network: NetworkSchema.optional()\n .describe(\"Network to query (defaults to configured network)\")\n },\n handler: async (agent: CronosAnalyticsAgent, input: Record<string, any>) => {\n try {\n const targetAgent = input.n