A Model Context Protocol (MCP) server that gives any MCP-compatible AI assistant (Claude, VS Code Copilot, Cursor, etc.) direct access to live Ethereum on-chain data. Query wallet balances, inspect transaction histories, debug failed transactions, simulate contract calls, and more — all through natural language.
- What is this?
- Features & Tools
- Tech Stack
- External APIs Used
- Project Structure
- Prerequisites
- Installation
- Configuration
- Running the Server
- Connecting to an MCP Client
- Tool Reference
- Supported Chains
- Environment Variables
- Troubleshooting
This project implements an MCP server — a standardized way to expose tools and capabilities to Large Language Models. Once connected, an AI assistant can call the tools in this server to fetch real blockchain data on demand, without the user having to leave their chat interface.
It is built on top of FastMCP, communicates over stdio (standard input/output), and exposes 7 blockchain tools powered by the Etherscan, Tenderly, and GitHub APIs.
| # | Tool | What it does |
|---|---|---|
| 1 | get_eth_balance |
Returns the current ETH balance of any wallet address in both ETH and Wei |
| 2 | get_transactions |
Retrieves recent transaction history with direction (in/out), value, timestamp, and status |
| 3 | get_token_holdings |
Lists all ERC-20 tokens held by a wallet with their current balances |
| 4 | get_gas_price |
Live gas price feed — slow / average / fast estimates in Gwei with estimated USD cost |
| 5 | debug_transaction |
Full transaction debugger: complete call trace, revert reason, gas used per call, decoded inputs |
| 6 | get_contract_source |
Fetches the verified Solidity source code of any contract and locates its GitHub repository |
| 7 | simulate_contract |
Simulates any contract function call — returns execution status, gas used, asset changes, and emitted events without broadcasting a real transaction |
| Component | Technology |
|---|---|
| Language | Python 3.11+ |
| MCP Framework | FastMCP via mcp[cli] |
| HTTP Client | httpx (async) |
| ABI Encoding | eth-abi |
| Keccak Hashing | pycryptodome |
| Environment Config | python-dotenv |
| Package Manager | uv (recommended) |
| Transport | stdio |
- Endpoint:
https://api.etherscan.io/v2/api - Used for: ETH balance, transaction list, ERC-20 token transfers, token balances, gas oracle, contract source code verification
- Requires:
ETHERSCAN_API_KEY - Get a key: https://etherscan.io/apis
- Endpoint (public):
https://api.tenderly.co/api/v1/public-contract/{chain_id}/trace/{tx_hash} - Endpoint (authenticated):
https://api.tenderly.co/api/v1/account/{account}/project/{project}/... - Used for: Transaction call trace & debugging (
debug_transaction) and contract function simulation (simulate_contract) - Note: The public endpoint works without any API key. Authenticated access is optional and gives higher rate limits.
- Requires (optional):
TENDERLY_API_KEY,TENDERLY_ACCOUNT,TENDERLY_PROJECT - Get a key: https://dashboard.tenderly.co
- Endpoint:
https://api.github.com - Used for: Searching for the GitHub repository of a verified contract and fetching its
.solfile - Requires (optional):
GITHUB_API_KEY— unauthenticated requests have a lower rate limit (60 req/hr) - Get a token: https://github.com/settings/tokens
- Endpoint:
https://www.4byte.directory/api/v1/signatures/ - Used for: Decoding raw 4-byte function selectors into human-readable signatures (e.g.
0xa9059cbb→transfer(address,uint256)) - Requires: No API key
- Endpoint:
https://eth-mainnet.g.alchemy.com/v2/{api_key} - Used for: Advanced JSON-RPC calls (
debug_traceTransaction, etc.) - Requires:
ALCHEMY_API_KEY - Get a key: https://www.alchemy.com
Etherscan_MCP/
│
├── server.py # FastMCP server entry point — registers all 7 tools
├── pyproject.toml # Project metadata & dependencies
├── .env # Your API keys (NOT committed to git)
├── .env.example # Template for required env vars
│
└── tools/
├── __init__.py # Exports all tool functions
├── etherscan_client.py # Shared Etherscan V2 HTTP client
├── tenderly_client.py # Tenderly trace client (public + authenticated)
├── alchemy_client.py # Alchemy JSON-RPC client
├── fourbyte_client.py # 4byte.directory function selector decoder
│
├── get_balance.py # Tool: ETH balance lookup
├── get_transactions.py # Tool: Transaction history
├── get_token_holdings.py # Tool: ERC-20 token holdings
├── get_gas_price.py # Tool: Live gas price oracle
├── debug_transaction.py # Tool: Full transaction debugger
├── get_contract_source.py # Tool: Verified contract source + GitHub lookup
└── simulate_contract.py # Tool: Contract function simulator
- Python 3.11 or higher
uvpackage manager (recommended) — install with:Or via pip:curl -LsSf https://astral.sh/uv/install.sh | shpip install uv
git clone https://github.com/your-username/Etherscan_MCP.git
cd Etherscan_MCPuv syncUsing pip instead? Run:
pip install -e .
Create a .env file in the project root by copying the example below:
cp .env.example .envThen fill in your API keys:
# ─── Required ─────────────────────────────────────
ETHERSCAN_API_KEY=your_etherscan_api_key_here
# ─── Optional (Tenderly — authenticated access) ───
# Without these, the public Tenderly endpoint is used (lower rate limits)
TENDERLY_API_KEY=your_tenderly_access_key
TENDERLY_ACCOUNT=your_tenderly_account_slug
TENDERLY_PROJECT=your_tenderly_project_slug
# ─── Optional (GitHub — higher rate limits) ───────
GITHUB_API_KEY=your_github_personal_access_token
# ─── Optional (Alchemy — for future RPC tools) ────
ALCHEMY_API_KEY=your_alchemy_api_key| Key | Where to get it |
|---|---|
ETHERSCAN_API_KEY |
etherscan.io/apis → Register → Create API Key |
TENDERLY_API_KEY |
dashboard.tenderly.co → Settings → Authorization → Access Keys |
TENDERLY_ACCOUNT |
Your Tenderly dashboard URL slug: dashboard.tenderly.co/{account}/... |
TENDERLY_PROJECT |
Your project slug inside Tenderly |
GITHUB_API_KEY |
github.com/settings/tokens → Generate new token (no special scopes needed for public repos) |
ALCHEMY_API_KEY |
alchemy.com → Create App → Copy API Key |
uv run python server.pyuv run mcp run server.pyuv run mcp dev server.pyThis opens the MCP Inspector in your browser where you can test all 7 tools interactively.
The server communicates over stdio. It should not be run as a standalone HTTP server — it is designed to be launched as a subprocess by an MCP client (Claude Desktop, VS Code Copilot, Cursor, etc.).
Add the following to your Claude Desktop config file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"etherscan-mcp": {
"command": "uv",
"args": [
"run",
"--directory",
"/absolute/path/to/Etherscan_MCP",
"python",
"server.py"
],
"env": {
"ETHERSCAN_API_KEY": "your_key_here"
}
}
}
}Replace
/absolute/path/to/Etherscan_MCPwith the actual path on your machine.
Add to your VS Code settings.json or workspace .vscode/mcp.json:
{
"servers": {
"etherscan-mcp": {
"type": "stdio",
"command": "uv",
"args": [
"run",
"--directory",
"/absolute/path/to/Etherscan_MCP",
"python",
"server.py"
],
"env": {
"ETHERSCAN_API_KEY": "your_key_here"
}
}
}
}Add to ~/.cursor/mcp.json:
{
"mcpServers": {
"etherscan-mcp": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/Etherscan_MCP", "python", "server.py"]
}
}
}Claude Code uses a project-level or global MCP config. Run the following command to add the server:
claude mcp add etherscan-mcp \
--command uv \
--args "run" "--directory" "/absolute/path/to/Etherscan_MCP" "python" "server.py" \
--env ETHERSCAN_API_KEY=your_key_hereOr add it manually to your Claude Code MCP config file:
Project-level (shared with your team): .claude/mcp.json in your repo root
Global (personal, all projects): ~/.claude/mcp.json
{
"mcpServers": {
"etherscan-mcp": {
"command": "uv",
"args": [
"run",
"--directory",
"/absolute/path/to/Etherscan_MCP",
"python",
"server.py"
],
"env": {
"ETHERSCAN_API_KEY": "your_key_here",
"TENDERLY_API_KEY": "your_tenderly_key",
"TENDERLY_ACCOUNT": "your_tenderly_account",
"TENDERLY_PROJECT": "your_tenderly_project"
}
}
}
}After adding, verify the server is connected by running:
claude mcp listYou should see etherscan-mcp listed with a connected status. You can then use it directly in a Claude Code session — for example:
> What is the ETH balance of vitalik.eth?
> Debug this transaction: 0x...
> Simulate calling transfer() on USDC for 100 tokens
Get the current ETH balance of any Ethereum wallet address.
| Parameter | Type | Required | Description |
|---|---|---|---|
address |
string | Yes | Ethereum wallet address (e.g. 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045) |
Returns: Balance in ETH and Wei.
Get the recent transaction history of any Ethereum wallet.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
address |
string | Yes | — | Ethereum wallet address |
limit |
integer | No | 10 | Number of transactions to return (max: 100) |
Returns: Formatted list with hash, direction (IN/OUT), ETH value, timestamp, and status.
Get all ERC-20 token holdings of a wallet address.
| Parameter | Type | Required | Description |
|---|---|---|---|
address |
string | Yes | Ethereum wallet address |
Returns: Token name, symbol, and current balance for each non-zero holding.
Get current Ethereum network gas prices. No parameters required.
Returns: Slow / average / fast gas estimates in Gwei, base fee, network usage ratio, and estimated USD cost for a standard transfer.
Full transaction debugger — like Tenderly's UI but in your AI assistant.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
tx_hash |
string | Yes | — | Transaction hash (0x...) |
chain_id |
integer | No | 1 | EVM chain ID |
Returns: Complete call trace tree, function names, decoded inputs, gas used per call, and exact revert reason (supports both Error(string) and Panic(uint256) Solidity errors).
Fetch the verified Solidity source code of any contract and locate its GitHub repository.
| Parameter | Type | Required | Description |
|---|---|---|---|
address |
string | Yes | Ethereum contract address |
Returns: Contract name, compiler version, license, full source code, and GitHub repository link (if found). Returns a clear message if the contract is not verified on Etherscan.
Simulate any contract function call without sending a real transaction.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
contract_address |
string | Yes | — | Target contract address |
function_name |
string | Yes | — | Function to call (e.g. transfer, approve) |
from_address |
string | Yes | — | Wallet to simulate the call from |
args |
string | No | "" |
Comma-separated arguments (e.g. "0xABC123,1000000") |
value_eth |
float | No | 0.0 |
Native ETH to send with the call |
chain |
string | No | "ethereum" |
Chain name or ID |
Returns: Execution status (success/fail), gas used, decoded asset changes (ERC-20 transfers), balance changes, emitted events, and call trace.
Automatically detects proxy contracts and resolves the implementation address. Also injects ERC-20 storage overrides when balance/allowance errors are detected, so transfer simulations work without needing prior approvals.
| Chain ID | Network |
|---|---|
1 |
Ethereum Mainnet |
10 |
Optimism |
137 |
Polygon |
8453 |
Base |
42161 |
Arbitrum One |
421614 |
Arbitrum Sepolia |
11155111 |
Ethereum Sepolia |
| Name | Chain ID |
|---|---|
ethereum |
1 |
bsc |
56 |
polygon |
137 |
arbitrum |
42161 |
optimism |
10 |
base |
8453 |
avalanche |
43114 |
fantom |
250 |
sepolia |
11155111 |
You can pass either the name (e.g. "arbitrum") or the numeric chain ID (e.g. 42161).
| Variable | Required | Description |
|---|---|---|
ETHERSCAN_API_KEY |
Yes | Powers all balance, transaction, gas, and contract source lookups |
TENDERLY_API_KEY |
No | Authenticated Tenderly access (required for simulate_contract) |
TENDERLY_ACCOUNT |
No | Tenderly account slug (required with TENDERLY_API_KEY) |
TENDERLY_PROJECT |
No | Tenderly project slug (required with TENDERLY_API_KEY) |
GITHUB_API_KEY |
No | GitHub personal access token (avoids 60 req/hr anonymous rate limit) |
ALCHEMY_API_KEY |
No | Alchemy API key (for future RPC-based tools) |
ETHERSCAN_API_KEY not set
→ Make sure your .env file exists in the project root and contains ETHERSCAN_API_KEY=.... The server loads it automatically via python-dotenv.
Error fetching transactions: Etherscan API error: Max rate limit reached
→ Free Etherscan API keys are limited to 5 calls/sec. Wait a moment and retry, or upgrade your Etherscan plan.
simulate_contract fails with authentication error
→ Contract simulation requires authenticated Tenderly credentials. Set TENDERLY_API_KEY, TENDERLY_ACCOUNT, and TENDERLY_PROJECT in your .env.
debug_transaction returns no data
→ The public Tenderly endpoint may not support all chains. Try providing TENDERLY_API_KEY + TENDERLY_ACCOUNT + TENDERLY_PROJECT for authenticated access which covers more chains.
Tools not appearing in Claude / VS Code Copilot
→ Double-check the absolute path in your MCP client config. Restart the client after saving changes. Run uv run mcp dev server.py to verify the server starts and all 7 tools are listed.
ModuleNotFoundError
→ Run uv sync again to ensure all dependencies are installed into the project virtual environment.