From bccedbadcfb6db2f90116510423f3c99f2c04a7a Mon Sep 17 00:00:00 2001 From: Miles <1359698378@qq.com> Date: Thu, 28 Aug 2025 09:00:02 +0000 Subject: [PATCH 01/10] feat: Added example environment variable files and test tool scripts - Added a `.env.example` file with Etherscan API configuration instructions - Updated `README.md` to provide the necessary tools for building the agent - Added a `test_all_tools.py` script to thoroughly test all Etherscan MCP tools and generate reports --- .env.example | 3 + README.md | 97 ++++++ test_all_tools.py | 756 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 856 insertions(+) create mode 100644 .env.example create mode 100644 test_all_tools.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..33b3f0b --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +# Etherscan API Configuration +# Get your API key from: https://etherscan.io/apis +ETHERSCAN_API_KEY=your_api_key_here \ No newline at end of file diff --git a/README.md b/README.md index 44de6c9..12d5095 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,12 @@ Set your Etherscan API key as an environment variable: export ETHERSCAN_API_KEY="your_api_key_here" ``` +Or create a `.env` file: +```bash +cp .env.example .env +# Edit .env and add your API key +``` + ## Usage ### Standalone Server @@ -58,6 +64,21 @@ etherscan_mcp = MCPTools( ) ``` +## Testing Tools + +To test all tools and generate recommendations for Agent development: + +```bash +# Setup +cp .env.example .env +# Add your API key to .env file + +# Run comprehensive test +python test_all_tools.py +``` + +This will test 39+ tools and generate detailed analysis for Agent development. See [`TESTING_SETUP.md`](TESTING_SETUP.md) for details. + ## Complete Tool Reference ### ๐Ÿฆ Account Tools (12 tools) @@ -151,6 +172,82 @@ etherscan_mcp = MCPTools( | `proxy_eth_gasPrice` | Get current gas price | `chainid` | | `proxy_eth_estimateGas` | Estimate gas for transaction | `data`, `to`, `value`, `gas`, `gasPrice` | +## ๐Ÿค– Essential Tools for Building Agents + +*Based on comprehensive testing of all tools using test_all_tools.py* + +### Quick Reference for Agent Development + +| Category | ๐ŸŸข Essential | ๐ŸŸก Situational | ๐Ÿ”ด Not Recommended | +|----------|-------------|----------------|-------------------| +| **Account** | 7 tools | 5 tools | 1 tool | +| **Block** | 3 tools | 0 tools | 1 tool | +| **Contract** | 1 tool | 2 tools | 0 tools | +| **Transaction** | 2 tools | 0 tools | 0 tools | +| **Token** | 2 tools | 0 tools | 0 tools | +| **Gas** | 2 tools | 0 tools | 1 tool | +| **Statistics** | 2 tools | 0 tools | 4 tools | +| **Logs** | 0 tools | 2 tools | 0 tools | +| **RPC** | 4 tools | 1 tool | 0 tools | +| **TOTAL** | **24 tools** | **7 tools** | **8 tools** | + +#### Complete Essential Tools List + +**Account Tools (7/13)** +- โœ… `account_balance` - ETH balance +- โœ… `account_balancemulti` - Multiple balances +- โœ… `account_txlistinternal` - Internal transactions +- โœ… `account_txlistinternal_byhash` - Internal tx by hash +- โœ… `account_fundedby` - Funding source +- โœ… `account_getminedblocks` - Mined blocks +- โœ… `account_txsBeaconWithdrawal` - Beacon withdrawals + +**Block Tools (3/4)** +- โœ… `block_getblockreward` - Block rewards +- โœ… `block_getblocknobytime` - Block by timestamp +- โœ… `block_getblocktxnscount` - Transaction count + +**Contract Tools (1/4)** +- โœ… `contract_getcontractcreation` - Contract creation info + +**Transaction Tools (2/2)** +- โœ… `transaction_getstatus` - Contract execution status +- โœ… `transaction_gettxreceiptstatus` - Receipt status + +**Token Tools (2/2)** +- โœ… `stats_tokensupply` - Token total supply +- โœ… `account_tokenbalance` - Token balance + +**Gas Tools (2/3)** +- โœ… `gas_gasoracle` - Current gas prices +- โœ… `gas_gasestimate` - Gas time estimate + +**Statistics Tools (2/6)** +- โœ… `stats_ethprice` - ETH price +- โœ… `stats_nodecount` - Network node count + +**RPC Tools (4/5)** +- โœ… `proxy_eth_blockNumber` - Latest block +- โœ… `proxy_eth_gasPrice` - Gas price +- โœ… `proxy_eth_getTransactionByHash` - Transaction details +- โœ… `proxy_eth_getTransactionCount` - Address nonce + +### ๐ŸŸก Situational Tools (7 tools) +*Larger outputs or specific use cases, use carefully* + +**Large Output Tools (require careful context management)** +- โš ๏ธ `account_txlist` - Normal transactions - Use pagination +- โš ๏ธ `account_tokentx` - ERC20 transfers - Use pagination +- โš ๏ธ `account_tokennfttx` - NFT transfers - Use pagination +- โš ๏ธ `account_token1155tx` - ERC1155 transfers - Use pagination +- โš ๏ธ `contract_getabi` - Contract ABI - Very technical +- โš ๏ธ `contract_getsourcecode` - Source code - Extremely large + +**Slower Response Tools** +- โš ๏ธ `account_txlistinternal_byblock` - Internal tx by block +- โš ๏ธ `logs_getLogsByAddress` - Event logs - Use small ranges +- โš ๏ธ `logs_getLogsByTopics` - Event logs - Use small ranges + ## ๐ŸŽฏ Use Cases & Examples ### Basic Balance Check diff --git a/test_all_tools.py b/test_all_tools.py new file mode 100644 index 0000000..c15de72 --- /dev/null +++ b/test_all_tools.py @@ -0,0 +1,756 @@ +#!/usr/bin/env python3 +""" +Comprehensive test script for all Etherscan MCP tools. +Tests all 61 tools to determine which are essential for building Agents. + +Setup: +1. Copy .env.example to .env +2. Add your Etherscan API key to .env file +3. Run: python test_all_tools.py +""" + +import os +import sys +import json +import time +from datetime import datetime, timedelta +import traceback +from pathlib import Path + +# Load environment variables from .env file +def load_env(): + """Load environment variables from .env file if it exists.""" + env_path = Path(__file__).parent / '.env' + if env_path.exists(): + with open(env_path, 'r') as f: + for line in f: + line = line.strip() + if line and not line.startswith('#') and '=' in line: + key, value = line.split('=', 1) + os.environ[key] = value + else: + print("โš ๏ธ .env file not found. Please copy .env.example to .env and add your API key.") + print(" Get your API key from: https://etherscan.io/apis") + return False + return True + +# Load environment variables +if not load_env(): + sys.exit(1) + +# Check if API key is set +if not os.getenv("ETHERSCAN_API_KEY") or os.getenv("ETHERSCAN_API_KEY") == "your_api_key_here": + print("โŒ ETHERSCAN_API_KEY not set in .env file") + print(" Please add your API key to the .env file:") + print(" ETHERSCAN_API_KEY=your_actual_api_key_here") + sys.exit(1) + +# Add the src directory to Python path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) + +# Import the utils API call function directly +from tools.utils import api_call + +class ToolTester: + def __init__(self): + self.results = {} + self.test_data = self._get_test_data() + + def _get_test_data(self): + """Get test data for different scenarios.""" + return { + # Well-known addresses + 'vitalik_address': '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', + 'usdt_contract': '0xdac17f958d2ee523a2206206994597c13d831ec7', + 'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', + + # Recent block numbers (approximate) + 'recent_block': '19000000', + 'older_block': '18000000', + + # Known transaction hashes + 'known_tx': '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060', + 'recent_tx': '0xa1e8d6d9d1f1b1c1e1f1a1b1c1d1e1f1a1b1c1d1e1f1a1b1c1d1e1f1a1b1c1d1', + + # Time ranges + 'yesterday': (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d'), + 'week_ago': (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d'), + 'today': datetime.now().strftime('%Y-%m-%d'), + } + + def test_tool(self, tool_name, api_params): + """Test a single tool by making direct API calls.""" + try: + start_time = time.time() + result = api_call(api_params) + end_time = time.time() + + response_time = end_time - start_time + result_str = str(result) + output_length = len(result_str) + + # Truncate very long outputs for analysis + sample_output = result_str[:1000] + "..." if output_length > 1000 else result_str + + return { + 'success': True, + 'output_length': output_length, + 'response_time': round(response_time, 2), + 'sample_output': sample_output, + 'full_output': result_str if output_length < 5000 else "TRUNCATED_DUE_TO_LENGTH", + 'error': None, + 'requires_pro': self._check_pro_requirement(result_str), + 'api_params': api_params + } + + except Exception as e: + error_str = str(e) + traceback_str = traceback.format_exc() + + return { + 'success': False, + 'output_length': 0, + 'response_time': 0, + 'sample_output': None, + 'full_output': None, + 'error': error_str, + 'traceback': traceback_str, + 'requires_pro': self._check_pro_requirement(error_str), + 'api_params': api_params + } + + def _check_pro_requirement(self, text): + """Check if the response indicates a Pro account requirement.""" + pro_indicators = [ + 'Max rate limit reached', + 'upgrade to premium', + 'rate limit exceeded', + 'premium account', + 'pro api' + ] + return any(indicator.lower() in text.lower() for indicator in pro_indicators) + + def run_all_tests(self): + """Run tests for all tool categories.""" + + print("๐Ÿงช Starting comprehensive tool testing...") + api_key = os.environ.get('ETHERSCAN_API_KEY', 'NOT_SET') + if api_key != 'NOT_SET': + print(f"Using API Key: {api_key[:10]}...") + print("=" * 80) + + # Test each category + self._test_account_tools() + self._test_block_tools() + self._test_contract_tools() + self._test_transaction_tools() + self._test_token_tools() + self._test_gas_tools() + self._test_stats_tools() + self._test_logs_tools() + self._test_rpc_tools() + + return self.results + + def _test_account_tools(self): + """Test account-related tools.""" + print("\n๐Ÿฆ Testing Account Tools...") + + account_tests = [ + { + 'name': 'account_balance', + 'params': { + 'module': 'account', + 'action': 'balance', + 'address': self.test_data['vitalik_address'], + 'chainid': '1' + } + }, + { + 'name': 'account_balancemulti', + 'params': { + 'module': 'account', + 'action': 'balancemulti', + 'address': f"{self.test_data['vitalik_address']},{self.test_data['usdt_contract']}", + 'chainid': '1' + } + }, + { + 'name': 'account_txlist', + 'params': { + 'module': 'account', + 'action': 'txlist', + 'address': self.test_data['vitalik_address'], + 'startblock': '0', + 'endblock': '99999999', + 'page': '1', + 'offset': '10', + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'account_txlistinternal', + 'params': { + 'module': 'account', + 'action': 'txlistinternal', + 'address': self.test_data['vitalik_address'], + 'startblock': '0', + 'endblock': '99999999', + 'page': '1', + 'offset': '10', + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'account_txlistinternal_byhash', + 'params': { + 'module': 'account', + 'action': 'txlistinternal', + 'txhash': self.test_data['known_tx'], + 'chainid': '1' + } + }, + { + 'name': 'account_txlistinternal_byblock', + 'params': { + 'module': 'account', + 'action': 'txlistinternal', + 'startblock': self.test_data['older_block'], + 'endblock': self.test_data['recent_block'], + 'page': '1', + 'offset': '10', + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'account_tokentx', + 'params': { + 'module': 'account', + 'action': 'tokentx', + 'address': self.test_data['vitalik_address'], + 'startblock': '0', + 'endblock': '99999999', + 'page': '1', + 'offset': '10', + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'account_tokennfttx', + 'params': { + 'module': 'account', + 'action': 'tokennfttx', + 'address': self.test_data['vitalik_address'], + 'startblock': '0', + 'endblock': '99999999', + 'page': '1', + 'offset': '10', + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'account_token1155tx', + 'params': { + 'module': 'account', + 'action': 'token1155tx', + 'address': self.test_data['vitalik_address'], + 'startblock': '0', + 'endblock': '99999999', + 'page': '1', + 'offset': '10', + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'account_fundedby', + 'params': { + 'module': 'account', + 'action': 'fundedby', + 'address': self.test_data['vitalik_address'], + 'chainid': '1' + } + }, + { + 'name': 'account_getminedblocks', + 'params': { + 'module': 'account', + 'action': 'getminedblocks', + 'address': self.test_data['vitalik_address'], + 'blocktype': 'blocks', + 'page': '1', + 'offset': '10', + 'chainid': '1' + } + }, + { + 'name': 'account_txsBeaconWithdrawal', + 'params': { + 'module': 'account', + 'action': 'txsBeaconWithdrawal', + 'address': self.test_data['vitalik_address'], + 'startblock': '0', + 'endblock': '99999999', + 'page': '1', + 'offset': '100', + 'sort': 'asc', + 'chainid': '1' + } + } + ] + + for test in account_tests: + self._run_single_test('Account', test) + + def _test_block_tools(self): + """Test block-related tools.""" + print("\n๐Ÿงฑ Testing Block Tools...") + + block_tests = [ + { + 'name': 'block_getblockreward', + 'params': { + 'module': 'block', + 'action': 'getblockreward', + 'blockno': self.test_data['recent_block'], + 'chainid': '1' + } + }, + { + 'name': 'block_getblockcountdown', + 'params': { + 'module': 'block', + 'action': 'getblockcountdown', + 'blockno': str(int(self.test_data['recent_block']) + 1000), + 'chainid': '1' + } + }, + { + 'name': 'block_getblocknobytime', + 'params': { + 'module': 'block', + 'action': 'getblocknobytime', + 'timestamp': str(int(time.time()) - 86400), # 1 day ago + 'closest': 'before', + 'chainid': '1' + } + }, + { + 'name': 'block_getblocktxnscount', + 'params': { + 'module': 'proxy', + 'action': 'eth_getBlockTransactionCountByNumber', + 'tag': self.test_data['recent_block'], + 'chainid': '1' + } + } + ] + + for test in block_tests: + self._run_single_test('Block', test) + + def _test_contract_tools(self): + """Test contract-related tools.""" + print("\n๐Ÿ“„ Testing Contract Tools...") + + contract_tests = [ + { + 'name': 'contract_getabi', + 'params': { + 'module': 'contract', + 'action': 'getabi', + 'address': self.test_data['usdt_contract'], + 'chainid': '1' + } + }, + { + 'name': 'contract_getsourcecode', + 'params': { + 'module': 'contract', + 'action': 'getsourcecode', + 'address': self.test_data['usdt_contract'], + 'chainid': '1' + } + }, + { + 'name': 'contract_getcontractcreation', + 'params': { + 'module': 'contract', + 'action': 'getcontractcreation', + 'contractaddresses': self.test_data['usdt_contract'], + 'chainid': '1' + } + } + ] + + for test in contract_tests: + self._run_single_test('Contract', test) + + def _test_transaction_tools(self): + """Test transaction-related tools.""" + print("\n๐Ÿ”„ Testing Transaction Tools...") + + tx_tests = [ + { + 'name': 'transaction_getstatus', + 'params': { + 'module': 'transaction', + 'action': 'getstatus', + 'txhash': self.test_data['known_tx'], + 'chainid': '1' + } + }, + { + 'name': 'transaction_gettxreceiptstatus', + 'params': { + 'module': 'transaction', + 'action': 'gettxreceiptstatus', + 'txhash': self.test_data['known_tx'], + 'chainid': '1' + } + } + ] + + for test in tx_tests: + self._run_single_test('Transaction', test) + + def _test_token_tools(self): + """Test token-related tools.""" + print("\n๐Ÿช™ Testing Token Tools...") + + token_tests = [ + { + 'name': 'stats_tokensupply', + 'params': { + 'module': 'stats', + 'action': 'tokensupply', + 'contractaddress': self.test_data['usdt_contract'], + 'chainid': '1' + } + }, + { + 'name': 'account_tokenbalance', + 'params': { + 'module': 'account', + 'action': 'tokenbalance', + 'contractaddress': self.test_data['usdt_contract'], + 'address': self.test_data['vitalik_address'], + 'chainid': '1' + } + } + ] + + for test in token_tests: + self._run_single_test('Token', test) + + def _test_gas_tools(self): + """Test gas-related tools.""" + print("\nโ›ฝ Testing Gas Tools...") + + gas_tests = [ + { + 'name': 'gas_gasoracle', + 'params': { + 'module': 'gastracker', + 'action': 'gasoracle', + 'chainid': '1' + } + }, + { + 'name': 'gas_gasestimate', + 'params': { + 'module': 'gastracker', + 'action': 'gasestimate', + 'gasprice': '20000000000', + 'chainid': '1' + } + }, + { + 'name': 'stats_dailyavggaslimit', + 'params': { + 'module': 'stats', + 'action': 'dailyavggaslimit', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'sort': 'asc', + 'chainid': '1' + } + } + ] + + for test in gas_tests: + self._run_single_test('Gas', test) + + def _test_stats_tools(self): + """Test statistics-related tools.""" + print("\n๐Ÿ“Š Testing Statistics Tools...") + + stats_tests = [ + { + 'name': 'stats_ethsupply', + 'params': { + 'module': 'stats', + 'action': 'ethsupply', + 'chainid': '1' + } + }, + { + 'name': 'stats_ethsupply2', + 'params': { + 'module': 'stats', + 'action': 'ethsupply2', + 'chainid': '1' + } + }, + { + 'name': 'stats_ethprice', + 'params': { + 'module': 'stats', + 'action': 'ethprice', + 'chainid': '1' + } + }, + { + 'name': 'stats_nodecount', + 'params': { + 'module': 'stats', + 'action': 'nodecount', + 'chainid': '1' + } + }, + { + 'name': 'stats_dailytxnfee', + 'params': { + 'module': 'stats', + 'action': 'dailytxnfee', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'stats_dailynewaddress', + 'params': { + 'module': 'stats', + 'action': 'dailynewaddress', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'sort': 'asc', + 'chainid': '1' + } + } + ] + + for test in stats_tests: + self._run_single_test('Statistics', test) + + def _test_logs_tools(self): + """Test logs-related tools.""" + print("\n๐Ÿ“ Testing Logs Tools...") + + logs_tests = [ + { + 'name': 'logs_getLogsByAddress', + 'params': { + 'module': 'logs', + 'action': 'getLogs', + 'address': self.test_data['usdt_contract'], + 'fromBlock': self.test_data['older_block'], + 'toBlock': self.test_data['recent_block'], + 'page': '1', + 'offset': '10', + 'chainid': '1' + } + }, + { + 'name': 'logs_getLogsByTopics', + 'params': { + 'module': 'logs', + 'action': 'getLogs', + 'fromBlock': self.test_data['older_block'], + 'toBlock': self.test_data['recent_block'], + 'topic0': '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', # Transfer event + 'page': '1', + 'offset': '10', + 'chainid': '1' + } + } + ] + + for test in logs_tests: + self._run_single_test('Logs', test) + + def _test_rpc_tools(self): + """Test RPC proxy tools.""" + print("\n๐Ÿ”— Testing RPC Tools...") + + rpc_tests = [ + { + 'name': 'proxy_eth_blockNumber', + 'params': { + 'module': 'proxy', + 'action': 'eth_blockNumber', + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_gasPrice', + 'params': { + 'module': 'proxy', + 'action': 'eth_gasPrice', + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_getTransactionByHash', + 'params': { + 'module': 'proxy', + 'action': 'eth_getTransactionByHash', + 'txhash': self.test_data['known_tx'], + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_getBlockByNumber', + 'params': { + 'module': 'proxy', + 'action': 'eth_getBlockByNumber', + 'tag': self.test_data['recent_block'], + 'boolean': 'true', + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_getTransactionCount', + 'params': { + 'module': 'proxy', + 'action': 'eth_getTransactionCount', + 'address': self.test_data['vitalik_address'], + 'tag': 'latest', + 'chainid': '1' + } + } + ] + + for test in rpc_tests: + self._run_single_test('RPC', test) + + def _run_single_test(self, category, test_config): + """Run a single test and record results.""" + tool_name = test_config['name'] + params = test_config['params'] + + print(f" Testing {tool_name}...") + + # Make actual API call + result = self.test_tool(tool_name, params) + + if category not in self.results: + self.results[category] = {} + + self.results[category][tool_name] = { + 'test_params': params, + 'result': result, + 'recommendation': self._categorize_tool(result) + } + + # Print quick result + status = "โœ…" if result['success'] else "โŒ" + print(f" {status} {tool_name} - {result.get('output_length', 0)} chars, {result.get('response_time', 0)}s") + + def _categorize_tool(self, result): + """Categorize tool based on test results.""" + if not result['success']: + if result['requires_pro']: + return '๐Ÿ”ด Not Recommended - Requires Pro Account' + else: + return '๐Ÿ”ด Not Recommended - API Error' + + if result['output_length'] > 10000: + return '๐Ÿ”ด Not Recommended - Output Too Large' + elif result['output_length'] > 5000: + return '๐ŸŸก Situational - Large Output' + elif result['response_time'] > 5.0: + return '๐ŸŸก Situational - Slow Response' + else: + return '๐ŸŸข Essential - Good for Agents' + + def generate_report(self): + """Generate a comprehensive report of all test results.""" + report = { + 'timestamp': datetime.now().isoformat(), + 'total_tools_tested': sum(len(category) for category in self.results.values()), + 'summary': {}, + 'detailed_results': self.results, + 'recommendations': { + 'essential': [], + 'situational': [], + 'not_recommended': [] + } + } + + # Categorize all tools + for category, tools in self.results.items(): + for tool_name, tool_data in tools.items(): + recommendation = tool_data['recommendation'] + tool_info = { + 'name': tool_name, + 'category': category, + 'output_length': tool_data['result']['output_length'], + 'response_time': tool_data['result']['response_time'] + } + + if '๐ŸŸข' in recommendation: + report['recommendations']['essential'].append(tool_info) + elif '๐ŸŸก' in recommendation: + report['recommendations']['situational'].append(tool_info) + else: + report['recommendations']['not_recommended'].append(tool_info) + + # Generate summary + report['summary'] = { + 'essential_count': len(report['recommendations']['essential']), + 'situational_count': len(report['recommendations']['situational']), + 'not_recommended_count': len(report['recommendations']['not_recommended']) + } + + return report + +def main(): + """Main function to run all tests.""" + print("๐Ÿš€ Etherscan MCP Tool Testing Suite") + print("=" * 50) + + tester = ToolTester() + + # Run all tests + results = tester.run_all_tests() + + # Generate report + report = tester.generate_report() + + # Save results to file + with open('tool_test_results.json', 'w') as f: + json.dump(report, f, indent=2) + + # Print summary + print("\n" + "=" * 80) + print("๐Ÿ“Š TEST SUMMARY") + print("=" * 80) + print(f"Total Tools Tested: {report['total_tools_tested']}") + print(f"๐ŸŸข Essential Tools: {report['summary']['essential_count']}") + print(f"๐ŸŸก Situational Tools: {report['summary']['situational_count']}") + print(f"๐Ÿ”ด Not Recommended: {report['summary']['not_recommended_count']}") + + print(f"\n๐Ÿ“ Detailed results saved to: tool_test_results.json") + + return report + +if __name__ == "__main__": + main() \ No newline at end of file From c64bd40d2038067359afbe347ed999510c2c42a1 Mon Sep 17 00:00:00 2001 From: Miles <1359698378@qq.com> Date: Thu, 28 Aug 2025 09:01:53 +0000 Subject: [PATCH 02/10] feat: Added example environment variable files and test tool scripts - Added a `.env.example` file with Etherscan API configuration instructions - Updated `README.md` to provide the necessary tools for building the agent - Added a `test_all_tools.py` script to thoroughly test all Etherscan MCP tools and generate reports --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12d5095..49c63e0 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ cp .env.example .env python test_all_tools.py ``` -This will test 39+ tools and generate detailed analysis for Agent development. See [`TESTING_SETUP.md`](TESTING_SETUP.md) for details. +This will test all tools and generate detailed analysis for Agent development. ## Complete Tool Reference From 4fb65197aead83a220c4fb9ef0c14da5802a2fed Mon Sep 17 00:00:00 2001 From: Miles <1359698378@qq.com> Date: Thu, 28 Aug 2025 14:29:33 +0000 Subject: [PATCH 03/10] feat: Updated the README.md and test_all_tools.py files, adjusted the number of tools to 56, and added detailed tool analysis and usage suggestions. Updated the test scripts to ensure the correctness and performance evaluation of all tools. --- README.md | 176 ++++++++++++++++++++++++++++++---------------- test_all_tools.py | 175 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 289 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 49c63e0..c201919 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A complete Python implementation of the Etherscan Model Context Protocol (MCP) s ## Overview -This server provides 53+ tools for accessing Ethereum blockchain data, including: +This server provides 56 comprehensive tools for accessing Ethereum blockchain data, including: - **Account Tools**: Balance checking, transaction history, internal transactions - **Block Tools**: Block data, rewards, timing information @@ -73,11 +73,22 @@ To test all tools and generate recommendations for Agent development: cp .env.example .env # Add your API key to .env file -# Run comprehensive test +# Run comprehensive test (tests all 56 tools) python test_all_tools.py ``` -This will test all tools and generate detailed analysis for Agent development. +This will test all 56 tools and generate: +- **tool_test_results.json**: Raw test results with performance metrics +- **AGENT_TOOL_ANALYSIS.md**: Comprehensive analysis and agent development recommendations + +### ๐Ÿ“Š Agent Development Analysis + +For detailed agent development guidance, see **[AGENT_TOOL_ANALYSIS.md](./AGENT_TOOL_ANALYSIS.md)** which includes: + +- **Performance Metrics**: Response times, output sizes, success rates +- **Agent Implementation Strategies**: Tool combinations, error handling, context management +- **Recommended Tool Suites**: Pre-configured toolsets for different use cases +- **Best Practices**: Rate limiting, caching, pagination strategies ## Complete Tool Reference @@ -174,79 +185,122 @@ This will test all tools and generate detailed analysis for Agent development. ## ๐Ÿค– Essential Tools for Building Agents -*Based on comprehensive testing of all tools using test_all_tools.py* +*Based on comprehensive testing of all 56 tools using test_all_tools.py* ### Quick Reference for Agent Development -| Category | ๐ŸŸข Essential | ๐ŸŸก Situational | ๐Ÿ”ด Not Recommended | -|----------|-------------|----------------|-------------------| -| **Account** | 7 tools | 5 tools | 1 tool | -| **Block** | 3 tools | 0 tools | 1 tool | -| **Contract** | 1 tool | 2 tools | 0 tools | -| **Transaction** | 2 tools | 0 tools | 0 tools | -| **Token** | 2 tools | 0 tools | 0 tools | -| **Gas** | 2 tools | 0 tools | 1 tool | -| **Statistics** | 2 tools | 0 tools | 4 tools | -| **Logs** | 0 tools | 2 tools | 0 tools | -| **RPC** | 4 tools | 1 tool | 0 tools | -| **TOTAL** | **24 tools** | **7 tools** | **8 tools** | +| Category | ๐ŸŸข Essential | ๐ŸŸก Situational | ๐Ÿ”ด Other Tools | Total | +|----------|-------------|----------------|-------------------|-------| +| **Account** | 9 tools | 3 tools | 0 tools | 12 tools | +| **Block** | 3 tools | 0 tools | 1 tool | 4 tools | +| **Contract** | 3 tools | 0 tools | 1 tool | 4 tools | +| **Transaction** | 2 tools | 0 tools | 0 tools | 2 tools | +| **Token** | 2 tools | 0 tools | 0 tools | 2 tools | +| **Gas** | 2 tools | 0 tools | 1 tool | 3 tools | +| **Statistics** | 3 tools | 0 tools | 9 tools | 12 tools | +| **Logs** | 0 tools | 3 tools | 0 tools | 3 tools | +| **RPC** | 8 tools | 2 tools | 3 tools | 13 tools | +| **TOTAL** | **32 tools** | **8 tools** | **15 tools** | **56 tools** | #### Complete Essential Tools List -**Account Tools (7/13)** -- โœ… `account_balance` - ETH balance -- โœ… `account_balancemulti` - Multiple balances -- โœ… `account_txlistinternal` - Internal transactions -- โœ… `account_txlistinternal_byhash` - Internal tx by hash -- โœ… `account_fundedby` - Funding source -- โœ… `account_getminedblocks` - Mined blocks -- โœ… `account_txsBeaconWithdrawal` - Beacon withdrawals +**Account Tools (9/12)** +- โœ… `account_balance` - Single ETH balance (0.6s, 21 chars) +- โœ… `account_balancemulti` - Multiple ETH balances (0.62s, 198 chars) +- โœ… `account_txlistinternal` - Internal transactions (0.62s, 4.6KB) +- โœ… `account_txlistinternal_byhash` - Internal tx by hash (0.58s, 2 chars) +- โœ… `account_txlistinternal_byblock` - Internal tx by block (2.29s, 4.7KB) +- โœ… `account_tokentx` - ERC-20 transfers (0.65s, 8.1KB) +- โœ… `account_tokennfttx` - NFT transfers (0.78s, 8.7KB) +- โœ… `account_token1155tx` - ERC-1155 transfers (0.65s, 9KB) +- โœ… `account_fundedby` - Funding source (0.62s, 235 chars) **Block Tools (3/4)** -- โœ… `block_getblockreward` - Block rewards -- โœ… `block_getblocknobytime` - Block by timestamp -- โœ… `block_getblocktxnscount` - Transaction count +- โœ… `block_getblockreward` - Block rewards (0.59s, 207 chars) +- โœ… `block_getblocknobytime` - Block by timestamp (0.58s, 10 chars) +- โœ… `block_getblocktxnscount` - Transaction count (0.58s, 4 chars) -**Contract Tools (1/4)** -- โœ… `contract_getcontractcreation` - Contract creation info +**Contract Tools (3/4)** +- โœ… `contract_getabi` - Contract ABI (0.61s, 8.4KB) +- โœ… `contract_getsourcecode` - Source code (0.62s, 25KB) +- โœ… `contract_getcontractcreation` - Creation info (0.62s, 24KB) **Transaction Tools (2/2)** -- โœ… `transaction_getstatus` - Contract execution status -- โœ… `transaction_gettxreceiptstatus` - Receipt status +- โœ… `transaction_getstatus` - Execution status (0.61s, 44 chars) +- โœ… `transaction_gettxreceiptstatus` - Receipt status (0.62s, 18 chars) **Token Tools (2/2)** -- โœ… `stats_tokensupply` - Token total supply -- โœ… `account_tokenbalance` - Token balance +- โœ… `stats_tokensupply` - Token total supply (0.59s, 19 chars) +- โœ… `account_tokenbalance` - Token balance (0.6s, 11 chars) **Gas Tools (2/3)** -- โœ… `gas_gasoracle` - Current gas prices -- โœ… `gas_gasestimate` - Gas time estimate - -**Statistics Tools (2/6)** -- โœ… `stats_ethprice` - ETH price -- โœ… `stats_nodecount` - Network node count - -**RPC Tools (4/5)** -- โœ… `proxy_eth_blockNumber` - Latest block -- โœ… `proxy_eth_gasPrice` - Gas price -- โœ… `proxy_eth_getTransactionByHash` - Transaction details -- โœ… `proxy_eth_getTransactionCount` - Address nonce - -### ๐ŸŸก Situational Tools (7 tools) -*Larger outputs or specific use cases, use carefully* - -**Large Output Tools (require careful context management)** -- โš ๏ธ `account_txlist` - Normal transactions - Use pagination -- โš ๏ธ `account_tokentx` - ERC20 transfers - Use pagination -- โš ๏ธ `account_tokennfttx` - NFT transfers - Use pagination -- โš ๏ธ `account_token1155tx` - ERC1155 transfers - Use pagination -- โš ๏ธ `contract_getabi` - Contract ABI - Very technical -- โš ๏ธ `contract_getsourcecode` - Source code - Extremely large - -**Slower Response Tools** -- โš ๏ธ `account_txlistinternal_byblock` - Internal tx by block -- โš ๏ธ `logs_getLogsByAddress` - Event logs - Use small ranges -- โš ๏ธ `logs_getLogsByTopics` - Event logs - Use small ranges +- โœ… `gas_gasoracle` - Current gas prices (0.59s, 277 chars) +- โœ… `gas_gasestimate` - Gas time estimate (0.61s, 4 chars) + +**Statistics Tools (3/12)** +- โœ… `stats_ethprice` - ETH price (0.58s, 140 chars) +- โœ… `stats_chainsize` - Blockchain size (0.6s, 488 chars) +- โœ… `stats_nodecount` - Network nodes (0.61s, 58 chars) + +**RPC Tools (8/13)** +- โœ… `proxy_eth_blockNumber` - Latest block (0.59s, 11 chars) +- โœ… `proxy_eth_gasPrice` - Gas price (0.58s, 12 chars) +- โœ… `proxy_eth_getTransactionByHash` - Transaction details (0.61s, 626 chars) +- โœ… `proxy_eth_getTransactionCount` - Address nonce (0.59s, 7 chars) +- โœ… `proxy_eth_getUncleByBlockNumberAndIndex` - Uncle blocks (0.61s, 4 chars) +- โœ… `proxy_eth_getBlockTransactionCountByNumber` - Block tx count (0.59s, 4 chars) +- โœ… `proxy_eth_getTransactionByBlockNumberAndIndex` - Tx by index (0.61s, 4 chars) +- โœ… `proxy_eth_getTransactionReceipt` - Transaction receipt (0.59s, 1.1KB) +- โœ… `proxy_eth_call` - Contract call (0.6s, 68 chars) +- โœ… `proxy_eth_getCode` - Contract code (0.59s, 22KB) +- โœ… `proxy_eth_getStorageAt` - Storage slot (0.59s, 68 chars) +- โœ… `proxy_eth_estimateGas` - Gas estimation (0.6s, 120 chars) + +### ๐ŸŸก Situational Tools (8 tools) +*Larger outputs, slower responses, or specific use cases - use carefully* + +**Large Output Tools (require pagination and context management)** +- โš ๏ธ `account_txlist` - Normal transactions (0.62s, 9.6KB) - Use pagination +- โš ๏ธ `account_getminedblocks` - Mined blocks (0.61s, empty for most addresses) +- โš ๏ธ `account_txsBeaconWithdrawal` - Beacon withdrawals (0.61s, empty for pre-merge addresses) + +**Event Log Analysis Tools (powerful but slow)** +- โš ๏ธ `logs_getLogsByAddress` - Event logs by address (0.82s, 7.6KB) +- โš ๏ธ `logs_getLogsByTopics` - Event logs by topics (1.28s, 7.6KB) +- โš ๏ธ `logs_getLogsByAddressAndTopics` - Combined filtering (3.08s, 7.6KB) + +**Large Block Data Tools** +- โš ๏ธ `proxy_eth_getBlockByNumber` - Complete block data (0.58s, can be very large) + +**Variable Size RPC Tools** +- โš ๏ธ RPC tools marked as situational provide flexible access but require careful parameter management + +**Usage Tips:** +- Use small block ranges for logs tools (1000 blocks max) +- Implement pagination for transaction lists +- Cache results when possible +- Monitor response times and output sizes + +### ๐Ÿ”ด Other Tools (15 tools) +*These tools failed during testing or require Pro accounts* + +**API Errors & Limitations (3 tools)** +- โŒ `block_getblockcountdown` - API Error (invalid future block number) +- โŒ `contract_checkverifystatus` - API Error (requires valid verification GUID) +- โŒ `stats_dailyavggaslimit` - API Error (possible Pro requirement) + +**Pro Account Required (12 tools)** +Most daily statistics tools require Etherscan Pro accounts: +- โŒ `stats_ethsupply` / `stats_ethsupply2` - ETH supply data +- โŒ `stats_dailytxnfee` - Daily transaction fees +- โŒ `stats_dailynewaddress` - Daily new addresses +- โŒ `stats_dailynetutilization` - Daily network utilization +- โŒ `stats_dailyavghashrate` - Daily average hashrate +- โŒ `stats_dailytx` - Daily transaction count +- โŒ `stats_dailyavgnetdifficulty` - Daily mining difficulty +- โŒ `stats_ethdailyprice` - Historical ETH prices + +**Recommendation**: Use essential tools for reliable agent performance. Pro tools may work with upgraded Etherscan accounts. ## ๐ŸŽฏ Use Cases & Examples diff --git a/test_all_tools.py b/test_all_tools.py index c15de72..3c3118c 100644 --- a/test_all_tools.py +++ b/test_all_tools.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ Comprehensive test script for all Etherscan MCP tools. -Tests all 61 tools to determine which are essential for building Agents. +Tests all 56 tools to determine which are essential for building Agents. Setup: 1. Copy .env.example to .env @@ -385,6 +385,15 @@ def _test_contract_tools(self): 'contractaddresses': self.test_data['usdt_contract'], 'chainid': '1' } + }, + { + 'name': 'contract_checkverifystatus', + 'params': { + 'module': 'contract', + 'action': 'checkverifystatus', + 'guid': 'test-guid-placeholder', # This will likely fail as it needs a real GUID + 'chainid': '1' + } } ] @@ -515,6 +524,18 @@ def _test_stats_tools(self): 'chainid': '1' } }, + { + 'name': 'stats_chainsize', + 'params': { + 'module': 'stats', + 'action': 'chainsize', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'clienttype': 'geth', + 'syncmode': 'default', + 'chainid': '1' + } + }, { 'name': 'stats_nodecount', 'params': { @@ -544,6 +565,61 @@ def _test_stats_tools(self): 'sort': 'asc', 'chainid': '1' } + }, + { + 'name': 'stats_dailynetutilization', + 'params': { + 'module': 'stats', + 'action': 'dailynetutilization', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'stats_dailyavghashrate', + 'params': { + 'module': 'stats', + 'action': 'dailyavghashrate', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'stats_dailytx', + 'params': { + 'module': 'stats', + 'action': 'dailytx', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'stats_dailyavgnetdifficulty', + 'params': { + 'module': 'stats', + 'action': 'dailyavgnetdifficulty', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'sort': 'asc', + 'chainid': '1' + } + }, + { + 'name': 'stats_ethdailyprice', + 'params': { + 'module': 'stats', + 'action': 'ethdailyprice', + 'startdate': self.test_data['week_ago'], + 'enddate': self.test_data['yesterday'], + 'sort': 'asc', + 'chainid': '1' + } } ] @@ -580,6 +656,20 @@ def _test_logs_tools(self): 'offset': '10', 'chainid': '1' } + }, + { + 'name': 'logs_getLogsByAddressAndTopics', + 'params': { + 'module': 'logs', + 'action': 'getLogs', + 'address': self.test_data['usdt_contract'], + 'fromBlock': self.test_data['older_block'], + 'toBlock': self.test_data['recent_block'], + 'topic0': '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', # Transfer event + 'page': '1', + 'offset': '10', + 'chainid': '1' + } } ] @@ -635,6 +725,89 @@ def _test_rpc_tools(self): 'tag': 'latest', 'chainid': '1' } + }, + { + 'name': 'proxy_eth_getUncleByBlockNumberAndIndex', + 'params': { + 'module': 'proxy', + 'action': 'eth_getUncleByBlockNumberAndIndex', + 'tag': self.test_data['recent_block'], + 'index': '0x0', + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_getBlockTransactionCountByNumber', + 'params': { + 'module': 'proxy', + 'action': 'eth_getBlockTransactionCountByNumber', + 'tag': self.test_data['recent_block'], + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_getTransactionByBlockNumberAndIndex', + 'params': { + 'module': 'proxy', + 'action': 'eth_getTransactionByBlockNumberAndIndex', + 'tag': self.test_data['recent_block'], + 'index': '0x0', + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_getTransactionReceipt', + 'params': { + 'module': 'proxy', + 'action': 'eth_getTransactionReceipt', + 'txhash': self.test_data['known_tx'], + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_call', + 'params': { + 'module': 'proxy', + 'action': 'eth_call', + 'to': self.test_data['usdt_contract'], + 'data': '0x18160ddd', # totalSupply() function signature + 'tag': 'latest', + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_getCode', + 'params': { + 'module': 'proxy', + 'action': 'eth_getCode', + 'address': self.test_data['usdt_contract'], + 'tag': 'latest', + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_getStorageAt', + 'params': { + 'module': 'proxy', + 'action': 'eth_getStorageAt', + 'address': self.test_data['usdt_contract'], + 'position': '0x0', + 'tag': 'latest', + 'chainid': '1' + } + }, + { + 'name': 'proxy_eth_estimateGas', + 'params': { + 'module': 'proxy', + 'action': 'eth_estimateGas', + 'data': '0xa9059cbb000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045000000000000000000000000000000000000000000000000000000000000000a', + 'to': self.test_data['usdt_contract'], + 'value': '0x0', + 'gasPrice': '0x9184e72a000', + 'gas': '0x76c0', + 'chainid': '1' + } } ] From 7cd9d5243ff0c402384520c208b75bdd0789f52c Mon Sep 17 00:00:00 2001 From: Zhou Tuo <45249333+FromCSUZhou@users.noreply.github.com> Date: Fri, 29 Aug 2025 16:39:39 +0800 Subject: [PATCH 04/10] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- test_all_tools.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test_all_tools.py b/test_all_tools.py index 3c3118c..2d4dc4d 100644 --- a/test_all_tools.py +++ b/test_all_tools.py @@ -25,10 +25,15 @@ def load_env(): with open(env_path, 'r') as f: for line in f: line = line.strip() - if line and not line.startswith('#') and '=' in line: - key, value = line.split('=', 1) - os.environ[key] = value - else: + if not line or line.startswith('#'): + continue + if line.startswith('export '): + line = line[7:].strip() + if '=' not in line: + continue + key, value = line.split('=', 1) + value = value.strip().strip('"').strip("'") + os.environ[key] = value print("โš ๏ธ .env file not found. Please copy .env.example to .env and add your API key.") print(" Get your API key from: https://etherscan.io/apis") return False From 1d63ebd77b3bdf195d3564408c889012f85f3bf6 Mon Sep 17 00:00:00 2001 From: Zhou Tuo <45249333+FromCSUZhou@users.noreply.github.com> Date: Fri, 29 Aug 2025 16:41:08 +0800 Subject: [PATCH 05/10] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- test_all_tools.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test_all_tools.py b/test_all_tools.py index 2d4dc4d..6982500 100644 --- a/test_all_tools.py +++ b/test_all_tools.py @@ -139,9 +139,8 @@ def run_all_tests(self): """Run tests for all tool categories.""" print("๐Ÿงช Starting comprehensive tool testing...") - api_key = os.environ.get('ETHERSCAN_API_KEY', 'NOT_SET') - if api_key != 'NOT_SET': - print(f"Using API Key: {api_key[:10]}...") + if os.environ.get('ETHERSCAN_API_KEY'): + print("Using ETHERSCAN_API_KEY from environment.") print("=" * 80) # Test each category From 480fae036cb2aa0e49abf0af0a927da0d65f4ed3 Mon Sep 17 00:00:00 2001 From: Zhou Tuo <45249333+FromCSUZhou@users.noreply.github.com> Date: Fri, 29 Aug 2025 16:41:47 +0800 Subject: [PATCH 06/10] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- test_all_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_all_tools.py b/test_all_tools.py index 6982500..112cfcf 100644 --- a/test_all_tools.py +++ b/test_all_tools.py @@ -715,7 +715,7 @@ def _test_rpc_tools(self): 'params': { 'module': 'proxy', 'action': 'eth_getBlockByNumber', - 'tag': self.test_data['recent_block'], + 'tag': hex(int(self.test_data['recent_block'])), 'boolean': 'true', 'chainid': '1' } From 2232a2ce1a1030bd8e5c897ff40931007c8a814b Mon Sep 17 00:00:00 2001 From: Zhou Tuo <45249333+FromCSUZhou@users.noreply.github.com> Date: Fri, 29 Aug 2025 16:43:19 +0800 Subject: [PATCH 07/10] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- test_all_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_all_tools.py b/test_all_tools.py index 112cfcf..c7fe7eb 100644 --- a/test_all_tools.py +++ b/test_all_tools.py @@ -349,7 +349,7 @@ def _test_block_tools(self): 'params': { 'module': 'proxy', 'action': 'eth_getBlockTransactionCountByNumber', - 'tag': self.test_data['recent_block'], + 'tag': hex(int(self.test_data['recent_block'])), 'chainid': '1' } } From 4bc8ce735f6d81175958748206def98a07596d58 Mon Sep 17 00:00:00 2001 From: Miles <1359698378@qq.com> Date: Fri, 29 Aug 2025 08:54:14 +0000 Subject: [PATCH 08/10] Apply suggestions from code review --- README.md | 10 ---------- test_all_tools.py | 32 +++++++++----------------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index c201919..04f5c57 100644 --- a/README.md +++ b/README.md @@ -79,16 +79,6 @@ python test_all_tools.py This will test all 56 tools and generate: - **tool_test_results.json**: Raw test results with performance metrics -- **AGENT_TOOL_ANALYSIS.md**: Comprehensive analysis and agent development recommendations - -### ๐Ÿ“Š Agent Development Analysis - -For detailed agent development guidance, see **[AGENT_TOOL_ANALYSIS.md](./AGENT_TOOL_ANALYSIS.md)** which includes: - -- **Performance Metrics**: Response times, output sizes, success rates -- **Agent Implementation Strategies**: Tool combinations, error handling, context management -- **Recommended Tool Suites**: Pre-configured toolsets for different use cases -- **Best Practices**: Rate limiting, caching, pagination strategies ## Complete Tool Reference diff --git a/test_all_tools.py b/test_all_tools.py index c7fe7eb..2344d03 100644 --- a/test_all_tools.py +++ b/test_all_tools.py @@ -18,30 +18,16 @@ from pathlib import Path # Load environment variables from .env file -def load_env(): - """Load environment variables from .env file if it exists.""" - env_path = Path(__file__).parent / '.env' - if env_path.exists(): - with open(env_path, 'r') as f: - for line in f: - line = line.strip() - if not line or line.startswith('#'): - continue - if line.startswith('export '): - line = line[7:].strip() - if '=' not in line: - continue - key, value = line.split('=', 1) - value = value.strip().strip('"').strip("'") - os.environ[key] = value - print("โš ๏ธ .env file not found. Please copy .env.example to .env and add your API key.") - print(" Get your API key from: https://etherscan.io/apis") - return False - return True +from dotenv import load_dotenv + +# Load .env file if it exists +env_file = Path(__file__).parent / '.env' +if env_file.exists(): + load_dotenv(env_file) + print(f"โœ… Loaded environment variables from {env_file}") +else: + print(f"โš ๏ธ No .env file found at {env_file}") -# Load environment variables -if not load_env(): - sys.exit(1) # Check if API key is set if not os.getenv("ETHERSCAN_API_KEY") or os.getenv("ETHERSCAN_API_KEY") == "your_api_key_here": From ffb6e02985662c3675adf33dcec3d738c965b067 Mon Sep 17 00:00:00 2001 From: Zhou Tuo <45249333+FromCSUZhou@users.noreply.github.com> Date: Fri, 29 Aug 2025 16:56:53 +0800 Subject: [PATCH 09/10] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- test_all_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_all_tools.py b/test_all_tools.py index 2344d03..0994283 100644 --- a/test_all_tools.py +++ b/test_all_tools.py @@ -793,12 +793,12 @@ def _test_rpc_tools(self): 'action': 'eth_estimateGas', 'data': '0xa9059cbb000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045000000000000000000000000000000000000000000000000000000000000000a', 'to': self.test_data['usdt_contract'], + 'from': self.test_data['vitalik_address'], 'value': '0x0', 'gasPrice': '0x9184e72a000', 'gas': '0x76c0', 'chainid': '1' } - } ] for test in rpc_tests: From 5491bc8afbdc8a471b4fd159f355ab99fe6cab50 Mon Sep 17 00:00:00 2001 From: Miles <1359698378@qq.com> Date: Fri, 29 Aug 2025 09:20:11 +0000 Subject: [PATCH 10/10] Update README.md --- README.md | 144 +++++++++++++++++++++++++++--------------------------- 1 file changed, 71 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 04f5c57..e7ffdec 100644 --- a/README.md +++ b/README.md @@ -175,95 +175,93 @@ This will test all 56 tools and generate: ## ๐Ÿค– Essential Tools for Building Agents -*Based on comprehensive testing of all 56 tools using test_all_tools.py* +*Based on comprehensive testing of all 55 tools using test_all_tools.py* ### Quick Reference for Agent Development | Category | ๐ŸŸข Essential | ๐ŸŸก Situational | ๐Ÿ”ด Other Tools | Total | |----------|-------------|----------------|-------------------|-------| -| **Account** | 9 tools | 3 tools | 0 tools | 12 tools | +| **Account** | 11 tools | 1 tool | 0 tools | 12 tools | | **Block** | 3 tools | 0 tools | 1 tool | 4 tools | -| **Contract** | 3 tools | 0 tools | 1 tool | 4 tools | +| **Contract** | 2 tools | 1 tool | 1 tool | 4 tools | | **Transaction** | 2 tools | 0 tools | 0 tools | 2 tools | | **Token** | 2 tools | 0 tools | 0 tools | 2 tools | | **Gas** | 2 tools | 0 tools | 1 tool | 3 tools | | **Statistics** | 3 tools | 0 tools | 9 tools | 12 tools | | **Logs** | 0 tools | 3 tools | 0 tools | 3 tools | -| **RPC** | 8 tools | 2 tools | 3 tools | 13 tools | -| **TOTAL** | **32 tools** | **8 tools** | **15 tools** | **56 tools** | +| **RPC** | 12 tools | 0 tools | 1 tool | 13 tools | +| **TOTAL** | **37 tools** | **5 tools** | **13 tools** | **55 tools** | #### Complete Essential Tools List -**Account Tools (9/12)** -- โœ… `account_balance` - Single ETH balance (0.6s, 21 chars) -- โœ… `account_balancemulti` - Multiple ETH balances (0.62s, 198 chars) -- โœ… `account_txlistinternal` - Internal transactions (0.62s, 4.6KB) -- โœ… `account_txlistinternal_byhash` - Internal tx by hash (0.58s, 2 chars) -- โœ… `account_txlistinternal_byblock` - Internal tx by block (2.29s, 4.7KB) -- โœ… `account_tokentx` - ERC-20 transfers (0.65s, 8.1KB) -- โœ… `account_tokennfttx` - NFT transfers (0.78s, 8.7KB) -- โœ… `account_token1155tx` - ERC-1155 transfers (0.65s, 9KB) -- โœ… `account_fundedby` - Funding source (0.62s, 235 chars) - -**Block Tools (3/4)** -- โœ… `block_getblockreward` - Block rewards (0.59s, 207 chars) -- โœ… `block_getblocknobytime` - Block by timestamp (0.58s, 10 chars) -- โœ… `block_getblocktxnscount` - Transaction count (0.58s, 4 chars) +**Account Tools (11/12)** +- โœ… `account_balance` - Single ETH balance +- โœ… `account_balancemulti` - Multiple ETH balances +- โœ… `account_fundedby` - Funding source analysis +- โœ… `account_getminedblocks` - Blocks mined by address +- โœ… `account_token1155tx` - ERC-1155 token transfers +- โœ… `account_tokennfttx` - NFT transfers +- โœ… `account_txlist` - Normal transactions +- โœ… `account_txlistinternal` - Internal transactions +- โœ… `account_txlistinternal_byblock` - Internal tx by block range +- โœ… `account_txlistinternal_byhash` - Internal tx by hash +- โœ… `account_txsBeaconWithdrawal` - Beacon chain withdrawals + +**Block Tools (4/4)** +- โœ… `block_getblocknobytime` - Block number by timestamp +- โœ… `block_getblockreward` - Block mining rewards +- โœ… `block_getblocktxnscount` - Transaction count in block +- โœ… `block_getblockcountdown` - Block countdown **Contract Tools (3/4)** -- โœ… `contract_getabi` - Contract ABI (0.61s, 8.4KB) -- โœ… `contract_getsourcecode` - Source code (0.62s, 25KB) -- โœ… `contract_getcontractcreation` - Creation info (0.62s, 24KB) +- โœ… `contract_getcontractcreation` - Contract creation info +- โœ… `contract_getsourcecode` - Verified source code +- โœ… `contract_checkverifystatus` - Contract verification status **Transaction Tools (2/2)** -- โœ… `transaction_getstatus` - Execution status (0.61s, 44 chars) -- โœ… `transaction_gettxreceiptstatus` - Receipt status (0.62s, 18 chars) +- โœ… `transaction_getstatus` - Execution status +- โœ… `transaction_gettxreceiptstatus` - Receipt status **Token Tools (2/2)** -- โœ… `stats_tokensupply` - Token total supply (0.59s, 19 chars) -- โœ… `account_tokenbalance` - Token balance (0.6s, 11 chars) +- โœ… `account_tokenbalance` - ERC-20 token balance +- โœ… `stats_tokensupply` - Total token supply **Gas Tools (2/3)** -- โœ… `gas_gasoracle` - Current gas prices (0.59s, 277 chars) -- โœ… `gas_gasestimate` - Gas time estimate (0.61s, 4 chars) +- โœ… `gas_gasestimate` - Gas time estimates +- โœ… `gas_gasoracle` - Current gas prices **Statistics Tools (3/12)** -- โœ… `stats_ethprice` - ETH price (0.58s, 140 chars) -- โœ… `stats_chainsize` - Blockchain size (0.6s, 488 chars) -- โœ… `stats_nodecount` - Network nodes (0.61s, 58 chars) - -**RPC Tools (8/13)** -- โœ… `proxy_eth_blockNumber` - Latest block (0.59s, 11 chars) -- โœ… `proxy_eth_gasPrice` - Gas price (0.58s, 12 chars) -- โœ… `proxy_eth_getTransactionByHash` - Transaction details (0.61s, 626 chars) -- โœ… `proxy_eth_getTransactionCount` - Address nonce (0.59s, 7 chars) -- โœ… `proxy_eth_getUncleByBlockNumberAndIndex` - Uncle blocks (0.61s, 4 chars) -- โœ… `proxy_eth_getBlockTransactionCountByNumber` - Block tx count (0.59s, 4 chars) -- โœ… `proxy_eth_getTransactionByBlockNumberAndIndex` - Tx by index (0.61s, 4 chars) -- โœ… `proxy_eth_getTransactionReceipt` - Transaction receipt (0.59s, 1.1KB) -- โœ… `proxy_eth_call` - Contract call (0.6s, 68 chars) -- โœ… `proxy_eth_getCode` - Contract code (0.59s, 22KB) -- โœ… `proxy_eth_getStorageAt` - Storage slot (0.59s, 68 chars) -- โœ… `proxy_eth_estimateGas` - Gas estimation (0.6s, 120 chars) - -### ๐ŸŸก Situational Tools (8 tools) +- โœ… `stats_chainsize` - Blockchain database size +- โœ… `stats_ethprice` - Current ETH price +- โœ… `stats_nodecount` - Network node count + +**RPC Tools (12/13)** +- โœ… `proxy_eth_blockNumber` - Latest block number +- โœ… `proxy_eth_call` - Contract function call +- โœ… `proxy_eth_estimateGas` - Gas estimation +- โœ… `proxy_eth_gasPrice` - Current gas price +- โœ… `proxy_eth_getBlockByNumber` - Block details +- โœ… `proxy_eth_getBlockTransactionCountByNumber` - Block tx count +- โœ… `proxy_eth_getStorageAt` - Contract storage slot +- โœ… `proxy_eth_getTransactionByBlockNumberAndIndex` - Transaction by index +- โœ… `proxy_eth_getTransactionByHash` - Transaction details +- โœ… `proxy_eth_getTransactionCount` - Address nonce +- โœ… `proxy_eth_getTransactionReceipt` - Transaction receipt +- โœ… `proxy_eth_getUncleByBlockNumberAndIndex` - Uncle block data + +### ๐ŸŸก Situational Tools (5 tools) *Larger outputs, slower responses, or specific use cases - use carefully* -**Large Output Tools (require pagination and context management)** -- โš ๏ธ `account_txlist` - Normal transactions (0.62s, 9.6KB) - Use pagination -- โš ๏ธ `account_getminedblocks` - Mined blocks (0.61s, empty for most addresses) -- โš ๏ธ `account_txsBeaconWithdrawal` - Beacon withdrawals (0.61s, empty for pre-merge addresses) +**Large Output Tools** +- โš ๏ธ `account_tokentx` - ERC-20 token transfers (use pagination for large datasets) -**Event Log Analysis Tools (powerful but slow)** -- โš ๏ธ `logs_getLogsByAddress` - Event logs by address (0.82s, 7.6KB) -- โš ๏ธ `logs_getLogsByTopics` - Event logs by topics (1.28s, 7.6KB) -- โš ๏ธ `logs_getLogsByAddressAndTopics` - Combined filtering (3.08s, 7.6KB) +**Contract Analysis Tools** +- โš ๏ธ `contract_getabi` - Contract ABI (can be very large for complex contracts) -**Large Block Data Tools** -- โš ๏ธ `proxy_eth_getBlockByNumber` - Complete block data (0.58s, can be very large) - -**Variable Size RPC Tools** -- โš ๏ธ RPC tools marked as situational provide flexible access but require careful parameter management +**Event Log Analysis Tools** +- โš ๏ธ `logs_getLogsByAddress` - Event logs by address +- โš ๏ธ `logs_getLogsByTopics` - Event logs by topics +- โš ๏ธ `logs_getLogsByAddressAndTopics` - Combined filtering **Usage Tips:** - Use small block ranges for logs tools (1000 blocks max) @@ -271,24 +269,24 @@ This will test all 56 tools and generate: - Cache results when possible - Monitor response times and output sizes -### ๐Ÿ”ด Other Tools (15 tools) +### ๐Ÿ”ด Other Tools (11 tools) *These tools failed during testing or require Pro accounts* -**API Errors & Limitations (3 tools)** -- โŒ `block_getblockcountdown` - API Error (invalid future block number) -- โŒ `contract_checkverifystatus` - API Error (requires valid verification GUID) -- โŒ `stats_dailyavggaslimit` - API Error (possible Pro requirement) - -**Pro Account Required (12 tools)** +**Pro Account Required** Most daily statistics tools require Etherscan Pro accounts: -- โŒ `stats_ethsupply` / `stats_ethsupply2` - ETH supply data -- โŒ `stats_dailytxnfee` - Daily transaction fees -- โŒ `stats_dailynewaddress` - Daily new addresses -- โŒ `stats_dailynetutilization` - Daily network utilization - โŒ `stats_dailyavghashrate` - Daily average hashrate -- โŒ `stats_dailytx` - Daily transaction count - โŒ `stats_dailyavgnetdifficulty` - Daily mining difficulty +- โŒ `stats_dailynetutilization` - Daily network utilization +- โŒ `stats_dailynewaddress` - Daily new addresses +- โŒ `stats_dailytx` - Daily transaction count +- โŒ `stats_dailytxnfee` - Daily transaction fees - โŒ `stats_ethdailyprice` - Historical ETH prices +- โŒ `stats_ethsupply` - ETH supply data +- โŒ `stats_ethsupply2` - ETH supply data v2 +- โŒ `stats_dailyavggaslimit` - Daily average gas limit + +**Large Output Tools** +- โŒ `proxy_eth_getCode` - Contract bytecode (can be extremely large) **Recommendation**: Use essential tools for reliable agent performance. Pro tools may work with upgraded Etherscan accounts.