FM

foshougua/mcp

Developer tools
0 stars 0 forks Quality 38 Trend 0

A Model Context Protocol (MCP) server implementation in Rust that enables AI agents to query Ethereum balances and simulate token swaps on Uniswap.

Overview

A Model Context Protocol (MCP) server implementation in Rust that enables AI agents to query Ethereum balances and simulate token swaps on Uniswap. - - Query ETH and ERC20 token balances for any Ethereum address - - Get current token prices in USD or ETH from CoinGecko - - Simulate Uniswap V2/V3 token swaps without executing on-chain - Rust 1.70+ (with cargo) - Access to an Ethereum RPC endpoint (public endpoints or Infura/Alchemy) The .env file contains: - ETH_RPC_URL: Ethereum RPC endpoint (default: https://eth.llamarpc.com) - PORT: HTTP server port (default: 3000) - PRIVATE_KEY: Optional private key for wallet functionality (default: not set) : The .env file is already in .gitignore and will not be committed to version control. Never commit your private keys! The server starts an HTTP server on port 3000 (default). You can change the port by setting the PORT environment variable: The server provides an HTTP API that accepts JSON-RPC 2.0 requests.

README

Ethereum Trading MCP Server

A Model Context Protocol (MCP) server implementation in Rust that enables AI agents to query Ethereum balances and simulate token swaps on Uniswap.

Features

  • get_balance - Query ETH and ERC20 token balances for any Ethereum address
  • get_token_price - Get current token prices in USD or ETH from CoinGecko
  • swap_tokens - Simulate Uniswap V2/V3 token swaps without executing on-chain

Requirements

  • Rust 1.70+ (with cargo)
  • Access to an Ethereum RPC endpoint (public endpoints or Infura/Alchemy)

Setup

  1. Clone the repository:

    git clone 
    cd mpc
    
  2. Install dependencies:

    cargo build
    
  3. Set up environment variables:

    # Copy the example environment file
    cp .env.example .env
    
    # Edit .env file if needed
    # The default ETH_RPC_URL is already set to https://eth.llamarpc.com
    # Optionally uncomment and set PRIVATE_KEY for wallet functionality
    

    The .env file contains:

    • ETH_RPC_URL: Ethereum RPC endpoint (default: https://eth.llamarpc.com)
    • PORT: HTTP server port (default: 3000)
    • PRIVATE_KEY: Optional private key for wallet functionality (default: not set)

    Note: The .env file is already in .gitignore and will not be committed to version control. Never commit your private keys!

  4. Build the project:

    cargo build --release
    
  5. Run the server:

    cargo run --release
    

    The server starts an HTTP server on port 3000 (default). You can change the port by setting the PORT environment variable:

    PORT=8080 cargo run --release
    

Usage

The server provides an HTTP API that accepts JSON-RPC 2.0 requests. By default, it runs on http://localhost:3000.

Start the Server

# Start server (default port 3000)
cargo run --release

# Or specify a custom port
PORT=8080 cargo run --release

The server will display:

MCP Server starting on http://0.0.0.0:3000
Health check available at http://0.0.0.0:3000/health

Quick Start - Command Line Testing

Method 1: Using curl

# Health check
curl http://localhost:3000/health

# Test get_balance
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_balance","arguments":{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}}}'

Method 2: Using provided test scripts

# Run all example tests
./test_examples.sh

# Test a specific tool
./test_single.sh get_balance '{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}'
./test_single.sh get_token_price '{"token_symbol":"USDC"}'
./test_single.sh swap_tokens '{"from_token":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","to_token":"0xdAC17F958D2ee523a2206206994597C13D831ec7","amount":"100.0"}'

# Specify custom server URL
./test_single.sh get_balance '{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}' 'http://localhost:8080'

Method 3: Using HTTP clients

You can use any HTTP client (Postman, Insomnia, httpie, etc.) to test the API:

  • Endpoint: POST http://localhost:3000
  • Headers: Content-Type: application/json
  • Body: JSON-RPC 2.0 request

Here are example JSON-RPC requests:

Initialize

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {}
}

List Tools

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

HTTP API Examples

Base URL: http://localhost:3000 (default)

Get Balance - Query ETH Balance

# Using curl
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_balance","arguments":{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}}}'

# Pretty formatted (using jq)
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "get_balance",
      "arguments": {
        "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
      }
    }
  }'

JSON Request:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_balance",
    "arguments": {
      "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
    }
  }
}

Get Balance - Query ERC20 Token Balance

curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_balance","arguments":{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","token_address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}}}'

JSON Request:

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "get_balance",
    "arguments": {
      "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
      "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
    }
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"address\":\"0x742d35cc6634c0532925a3b844bc9e7595f0beb\",\"eth_balance\":0.5,\"token_balance\":1000.0,\"token_address\":\"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\",\"token_symbol\":\"USDC\",\"token_decimals\":6}"
      }
    ]
  }
}

Get Token Price

# By symbol
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_token_price","arguments":{"token_symbol":"USDC"}}}'

# By address
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_token_price","arguments":{"token_address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}}}'

JSON Request:

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "get_token_price",
    "arguments": {
      "token_symbol": "USDC"
    }
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"token_address\":\"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\",\"token_symbol\":\"USDC\",\"price_usd\":1.0,\"price_eth\":0.0005}"
      }
    ]
  }
}

Swap Tokens (Simulation)

curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"swap_tokens","arguments":{"from_token":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","to_token":"0xdAC17F958D2ee523a2206206994597C13D831ec7","amount":"100.0","slippage_tolerance":0.5}}}'

JSON Request:

{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "tools/call",
  "params": {
    "name": "swap_tokens",
    "arguments": {
      "from_token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "to_token": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
      "amount": "100.0",
      "slippage_tolerance": 0.5
    }
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 6,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"from_token\":\"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\",\"to_token\":\"0xdac17f958d2ee523a2206206994597c13d831ec7\",\"amount_in\":100.0,\"estimated_amount_out\":99.5,\"slippage_tolerance\":0.5,\"estimated_gas\":150000,\"estimated_gas_cost_eth\":0.001,\"route\":\"Uniswap V2: 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 -> 0xdac17f958d2ee523a2206206994597c13d831ec7\",\"simulation_successful\":true}"
      }
    ]
  }
}

Testing

Run All Tests

Run the test suite:

cargo test

This will run all unit tests. Tests that require network connections are marked with #[ignore] and will be skipped by default.

Test Coverage

The test suite includes:

get_balance tests:

  • Address parsing validation
  • ETH balance query (requires network - ignored by default)
  • ERC20 token balance query (requires network - ignored by default)
  • Input validation

get_token_price tests:

  • Common tokens configuration
  • Token price by symbol (requires network - ignored by default)
  • Token price by address (requires network - ignored by default)
  • WETH price validation (requires network - ignored by default)
  • Error handling for missing inputs

swap_tokens tests:

  • Input validation (addresses, amounts, slippage)
  • Encoding functions (U256, Address, Address array)
  • Router address validation
  • Swap simulation (requires network - ignored by default)
  • Default slippage tolerance

Run Integration Tests (Network Required)

To run tests that require network connections:

# Run all tests including network tests
cargo test -- --ignored

# Run a specific integration test
cargo test test_get_balance_eth -- --ignored

# Run all tests including ignored ones
cargo test -- --include-ignored

Note: Integration tests require:

  • Active internet connection
  • Access to Ethereum RPC endpoint (configured via ETH_RPC_URL)
  • Access to CoinGecko API (for price tests)

Design Decisions

  1. Ethers-rs over Alloy: Chose ethers 1.0 for better stability and ecosystem maturity, despite alloy being newer.

  2. JSON-RPC 2.0 Implementation: Implemented MCP server manually using JSON-RPC 2.0 protocol rather than using an MCP SDK, allowing full control over the protocol implementation.

  3. Decimal Precision: Used rust_decimal for all financial calculations to avoid floating-point precision errors when dealing with token amounts and prices.

  4. Swap Simulation: The swap_tokens tool constructs real Uniswap V2 swap transactions and uses eth_call to simulate them without executing on-chain, providing accurate estimates of output amounts and gas costs.

  5. Error Handling: Comprehensive error types with thiserror for better error propagation and debugging.

Known Limitations

  1. Token Symbol Resolution: Token symbol fetching may return “UNKNOWN” for some tokens due to contract call failures. In production, this should query a token registry or use a more robust method.

  2. Swap Output Parsing: The swap simulation output parsing is simplified - it extracts the first 32 bytes from the response. A production implementation would properly decode the Uniswap router’s return value.

  3. Token Decimals: Currently assumes 18 decimals for tokens in swap calculations. Production code should fetch decimals from the contract.

  4. Price Data: Uses CoinGecko free tier API. Rate limits may apply with high usage.

  5. Wallet Management: Basic wallet management is implemented but not fully integrated. Private keys are loaded from environment variables but not used for transaction signing yet.

Project Structure

mpc/
├── src/
│   ├── main.rs              # Entry point
│   ├── error.rs             # Error types
│   ├── ethereum.rs          # Ethereum client wrapper
│   ├── wallet.rs            # Wallet management
│   ├── mcp/
│   │   └── server.rs        # MCP server implementation
│   ├── tools/
│   │   ├── balance.rs       # get_balance tool
│   │   ├── price.rs         # get_token_price tool
│   │   └── swap.rs          # swap_tokens tool
│   └── abi/
│       └── ERC20.json       # ERC20 contract ABI
├── Cargo.toml
└── README.md

License

This project is provided as-is for educational purposes.

View the full README on GitHub

Install

This server does not publish a one-line install command.

Open the repository installation guide New to MCP? Start with the mcpskill.org guide →