ethereum-mcp-server

Dannieeth/ethereum-mcp-server

3.2

If you are the rightful owner of ethereum-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

Ethereum MCP Server is a Rust-based server implementation for Ethereum blockchain operations, enabling AI agents to interact with Ethereum for balance queries, token price fetching, and token swap simulations.

Tools
3
Resources
0
Prompts
0

Ethereum MCP Server

A Model Context Protocol (MCP) server implementation in Rust for Ethereum blockchain operations. This server enables AI agents to query balances, fetch token prices from Chainlink oracles, and simulate token swaps on Uniswap V2.

Supports both stdio (MCP standard) and HTTP (REST API) modes for flexible integration.

Features

MCP Tools

  1. get_balance - Query ETH and ERC20 token balances

    • Supports both native ETH and any ERC20 token
    • Returns formatted balances with proper decimals
    • Concurrent fetching of token metadata
  2. get_token_price - Get current token prices from Chainlink price feeds

    • Fetches real-time USD prices from Chainlink oracles
    • Calculates ETH-denominated prices
    • Includes data staleness checks
    • Cached with moka (60s TTL by default)
  3. swap_tokens - Simulate token swaps on Uniswap V2

    • Constructs real Uniswap V2 transactions
    • Simulates execution using eth_call (no on-chain execution)
    • Calculates price impact and slippage protection
    • Estimates gas costs

Architecture

Technology Stack

  • Rust with async/await (tokio runtime)
  • ethers-rs - Ethereum RPC client and contract bindings
  • axum + tower - HTTP server framework (for HTTP mode)
  • moka - In-memory async cache (can be replaced with Redis for distributed deployment)
  • tracing - Structured logging
  • serde - Serialization/deserialization
  • rust_decimal - Financial precision calculations
  • clap - Command-line argument parsing

Directory Structure

ethereum-mcp-server/
├── src/
│   ├── main.rs              # Entry point, mode selection, tokio runtime
│   ├── lib.rs               # Library root
│   ├── config.rs            # Configuration management
│   ├── error.rs             # Unified error types
│   ├── mcp/                 # MCP protocol layer
│   │   ├── server.rs        # JSON-RPC 2.0 server (stdio mode)
│   │   ├── http_server.rs   # HTTP JSON-RPC server (HTTP mode)
│   │   ├── handler.rs       # Tool call routing
│   │   └── schema.rs        # Protocol schemas
│   ├── services/            # Business logic layer
│   │   ├── balance.rs       # Balance query service
│   │   ├── price.rs         # Price query service (Chainlink)
│   │   └── swap.rs          # Swap simulation service
│   ├── providers/           # External provider layer
│   │   ├── ethereum.rs      # Ethereum RPC client
│   │   ├── chainlink.rs     # Chainlink oracle with moka cache
│   │   └── uniswap.rs       # Uniswap V2 interaction
│   ├── contracts/           # Smart contract ABIs
│   │   ├── erc20.rs
│   │   ├── chainlink_feed.rs
│   │   └── uniswap_router.rs
│   └── utils/               # Utilities
│       ├── decimal.rs       # Precision handling
│       ├── validation.rs    # Input validation
│       └── wallet.rs        # Wallet management
└── tests/
    └── integration/         # Integration tests

Setup

Prerequisites

  • Rust 1.70+ (install from https://rustup.rs)
  • Ethereum RPC endpoint (Infura, Alchemy, or public endpoint)

Installation

  1. Clone the repository:
git clone <repository-url>
cd ethereum-mcp-server
  1. Copy environment configuration:
cp .env.example .env
  1. Edit .env with your configuration:
# Required
RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY
PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001

# Optional
LOG_LEVEL=info
PRICE_CACHE_TTL_SECS=60
CACHE_MAX_CAPACITY=1000

⚠️ Security Warning: Never commit your .env file with real private keys!

  1. Build the project:
cargo build --release

Running

The server supports two modes:

1. stdio Mode (Default - MCP Standard)
cargo run --release

The server communicates via stdin/stdout using JSON-RPC 2.0 protocol.

📝 Note: When sending requests via command line, JSON must be on a single line. See and for practical examples.

2. HTTP Mode (REST API)
# Start HTTP server on default port (3000)
cargo run --release -- --mode http

# Or specify custom host and port
cargo run --release -- --mode http --host 0.0.0.0 --port 8080

The server provides HTTP JSON-RPC endpoints:

  • POST /rpc - JSON-RPC 2.0 endpoint for all tool calls
  • GET /health - Health check endpoint

📡 See for detailed HTTP API documentation, examples with curl/Python/JavaScript, and deployment guides.

CLI Options
OPTIONS:
  -m, --mode <MODE>    Server mode [default: stdio] [possible values: stdio, http]
      --host <HOST>    HTTP server host [default: 127.0.0.1]
  -p, --port <PORT>    HTTP server port [default: 3000]
  -h, --help           Print help
  -V, --version        Print version

Usage Examples

MCP Tool Call Examples

ⓘ The JSON examples below are formatted for readability. For actual command-line usage, compress them to single-line format (see ).

1. List Available Tools

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "get_balance",
        "description": "Query ETH and ERC20 token balances...",
        "input_schema": { ... }
      },
      {
        "name": "get_token_price",
        "description": "Get current token price...",
        "input_schema": { ... }
      },
      {
        "name": "swap_tokens",
        "description": "Simulate a token swap...",
        "input_schema": { ... }
      }
    ]
  }
}
2. Query ETH Balance

Request:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_balance",
    "arguments": {
      "wallet_address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
    }
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{
          \"wallet_address\": \"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\",
          \"token_symbol\": \"ETH\",
          \"token_name\": \"Ethereum\",
          \"decimals\": 18,
          \"balance\": \"1500000000000000000\",
          \"balance_formatted\": \"1.5 ETH\",
          \"balance_decimal\": \"1.5\"
        }"
      }
    ]
  }
}
3. Query ERC20 Token Balance

Request:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_balance",
    "arguments": {
      "wallet_address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
    }
  }
}
4. Get Token Price

Request:

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "get_token_price",
    "arguments": {
      "token": "ETH"
    }
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{
          \"token\": \"ETH\",
          \"usd_price\": 2450.75,
          \"decimals\": 8,
          \"round_id\": 12345,
          \"updated_at\": \"2024-01-15T10:30:00Z\",
          \"is_stale\": false
        }"
      }
    ]
  }
}
5. Simulate Token Swap

Request:

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "swap_tokens",
    "arguments": {
      "from_token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "to_token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
      "amount": "100",
      "slippage_tolerance": 0.5
    }
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{
          \"from_token\": \"0xA0b8...\",
          \"to_token\": \"0x6B17...\",
          \"from_symbol\": \"USDC\",
          \"to_symbol\": \"DAI\",
          \"amount_in\": \"100000000\",
          \"amount_in_formatted\": \"100 USDC\",
          \"amount_out\": \"99500000000000000000\",
          \"amount_out_formatted\": \"99.5 DAI\",
          \"min_amount_out\": \"99002500000000000000\",
          \"min_amount_out_formatted\": \"99.0025 DAI\",
          \"price_impact\": 0.12,
          \"slippage_tolerance\": 0.5,
          \"path\": [\"0xA0b8...\", \"0xC02a...\", \"0x6B17...\"],
          \"estimated_gas\": \"150000\",
          \"gas_price\": \"30000000000\",
          \"gas_cost\": \"4500000000000000\",
          \"gas_cost_eth\": \"0.0045\",
          \"simulation_successful\": true
        }"
      }
    ]
  }
}

Design Decisions

1. Async Architecture with Tokio

  • All I/O operations (RPC calls, file I/O) are fully asynchronous
  • Tokio's multi-threaded runtime handles concurrent requests efficiently
  • tokio::join! and tokio::try_join! enable parallel RPC calls (e.g., fetching token info and balance simultaneously)

2. Chainlink for Price Data

  • Chose Chainlink oracles for decentralized, tamper-proof price feeds
  • Pre-configured mainnet price feed addresses for major tokens (ETH, BTC, USDC, etc.)
  • Supports both symbol-based queries ("ETH") and address-based queries

3. Moka Cache (Upgradeable to Redis)

  • Uses moka for in-memory async caching with configurable TTL (default 60s)
  • Price data is cached to reduce RPC calls and improve performance
  • Architecture allows easy replacement with Redis for distributed deployments:
    • Cache layer is abstracted in ChainlinkProvider
    • Redis implementation template included in comments
    • Simply swap Cache<String, PriceData> with redis::aio::ConnectionManager

4. Uniswap V2 for Swap Simulation

  • Implemented Uniswap V2 Router integration (simpler than V3)
  • Constructs real swap transactions but only simulates via eth_call
  • Calculates price impact by comparing pool reserves vs execution price
  • V3 support can be added as an extension

5. Error Handling and Validation

  • Comprehensive error types using thiserror
  • Input validation for addresses, amounts, and slippage
  • Maps errors to appropriate JSON-RPC error codes

Known Limitations and Assumptions

  1. Network Support: Currently configured for Ethereum Mainnet only

    • Chainlink price feeds and Uniswap addresses are mainnet-specific
    • Can be extended to support testnets or other EVM chains
  2. Uniswap Version: Implements V2 only

    • V3 support requires more complex tick and liquidity range logic
    • V2 is sufficient for price discovery and basic swaps
  3. Private Key Management: Uses environment variable for private key

    • Suitable for development and testing
    • Production deployments should use secure key management systems (HSM, KMS)
  4. RPC Rate Limits: No built-in rate limiting

    • Public RPC endpoints may throttle requests
    • Recommend using paid providers (Alchemy, Infura) for production
  5. Cache Distribution: Moka cache is in-memory (single-instance)

    • For horizontal scaling, replace with Redis (implementation template provided)
    • Cache invalidation is TTL-based only (no event-based invalidation)
  6. Price Feed Coverage: Limited to pre-configured tokens

    • Feed Registry support is optional (requires configuration)
    • Tokens without Chainlink feeds will return "not found" errors
  7. Transaction Simulation: Uses eth_call for swap simulation

    • Does not account for MEV or sandwich attacks
    • Actual on-chain execution may differ from simulation
  8. Gas Estimation: Basic gas estimation only

    • Does not account for network congestion
    • No EIP-1559 support (uses legacy gas pricing)

Testing

Run Unit Tests

cargo test --lib

Run Integration Tests

cargo test --test '*'

Run with Specific RPC (Integration Tests)

RPC_URL=https://eth.llamarpc.com cargo test --test '*' -- --ignored

Development

Enable Debug Logging

RUST_LOG=debug cargo run

Format Code

cargo fmt

Lint

cargo clippy -- -D warnings

Future Enhancements

  • Redis cache support for distributed deployment
  • Uniswap V3 integration
  • Multi-chain support (Polygon, Arbitrum, etc.)
  • EIP-1559 gas estimation
  • WebSocket RPC support
  • Historical price data queries
  • Portfolio tracking features

License

MIT License - see LICENSE file for details

Contributing

Contributions are welcome! Please open an issue or submit a pull request.