A Model Context Protocol (MCP) server implemented in Rust that enables AI agents to query Ethereum balances and simulate token swaps on Uniswap.
Quick Start Guide - Get up and running quickly.
The server provides three MCP tools:
-
get_balance- Query ETH and ERC20 token balances- Input: wallet address, optional token contract address
- Output: balance information with proper decimals
-
get_token_price- Get current token price in USD and ETH- Input: token address (0x...) or symbol (e.g., "USDC", "WETH")
- Output: price data from CoinGecko API
-
swap_tokens- Simulate token swaps on Uniswap V2 or V3- Input: from_token, to_token, amount, slippage tolerance
- Output: simulation result showing estimated output and gas costs
- Note: Constructs real Uniswap transactions and simulates them using
eth_callwithout executing on-chain
src/
├── main.rs # Server entry point and transport configuration
├── server.rs # MCP server implementation with tool handlers
├── swap.rs # Uniswap V2/V3 swap simulation logic
└── bin/
├── client.rs # MCP client for testing
├── test_eth_balance.rs # Test for ETH balance queries
├── test_erc20_balance.rs # Test for ERC20 balance queries
├── test_token_price.rs # Test for token price queries
└── test_swap.rs # Test for swap simulations
- Rust (latest stable version)
- tokio - Async runtime
- ethers-rs - Ethereum RPC client library
- rmcp - MCP SDK for Rust
- rust_decimal - Financial precision handling
- tracing - Structured logging
- reqwest - HTTP client for price API calls
- ethabi - ABI encoding/decoding
-
Install Rust (if not already installed):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
-
Clone the repository:
git clone https://github.com/chenjjiaa/eth-mcp; cd eth-mcp
Create a .env file in the project root (or use environment variables):
# Ethereum RPC endpoint (required)
ETH_RPC_URL=https://eth.llamarpc.com
# Or use Infura/Alchemy:
# ETH_RPC_URL=https://mainnet.infura.io/v3/YOUR_API_KEY
# Server configuration (optional)
SERVER_HOST=127.0.0.1 # Default: 127.0.0.1
SERVER_PORT=0 # Default: 0 (stdio mode). Set to >0 for TCP mode
# Logging (optional)
RUST_LOG=info # Default: info. Options: trace, debug, info, warn, error
# Wallet address
WALLET_ADDRESS=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
# USDC contract addr
TOKEN_ADDRESS=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48Note: The server defaults to stdio mode (MCP standard) when SERVER_PORT=0. For TCP mode, set SERVER_PORT to a valid port number.
-
Build the project:
cargo b # Or for release: cargo b --release -
Run the server:
# Stdio mode (default, for MCP clients) cargo r --bin server # Or directly: ./target/debug/server
-
Run tests:
# Run all tests cargo test # Run specific test binaries cargo r --bin test_eth_balance cargo r --bin test_erc20_balance cargo r --bin test_token_price cargo r --bin test_swap
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_balance",
"arguments": {
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}
}
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"wallet_address\": \"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb\",\n \"token_address\": null,\n \"balance\": \"0.000000000000000000\",\n \"decimals\": 18,\n \"raw_balance\": \"0\"\n}"
}
]
}
}Request:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_balance",
"arguments": {
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
}
}Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"wallet_address\": \"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb\",\n \"token_address\": \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\",\n \"balance\": \"1000.000000\",\n \"decimals\": 6,\n \"raw_balance\": \"1000000000\"\n}"
}
]
}
}Request:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_token_price",
"arguments": {
"token": "USDC"
}
}
}Response:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"token\": \"USDC\",\n \"token_address\": null,\n \"price_usd\": \"1.000000\",\n \"price_eth\": \"0.000300\",\n \"last_updated\": null\n}"
}
]
}
}Request:
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "swap_tokens",
"arguments": {
"from_token": "ETH",
"to_token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"amount": "1.0",
"slippage_tolerance": "0.5",
"version": "v2"
}
}
}Response:
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"from_token\": \"ETH\",\n \"to_token\": \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\",\n \"input_amount\": \"1.0\",\n \"estimated_output\": \"3200.123456\",\n \"minimum_output\": \"3184.122842\",\n \"slippage_tolerance\": \"0.5\",\n \"estimated_gas\": \"150000\",\n \"estimated_gas_eth\": \"0.003000\",\n \"price_impact\": null,\n \"involves_eth\": true,\n \"version\": \"v2\"\n}"
}
]
}
}-
MCP Protocol Implementation: Used the
rmcpSDK to handle MCP protocol communication, which provides type-safe tool registration and request/response handling. The server supports both stdio (standard MCP mode) and TCP transport for flexibility. -
Ethereum Interaction: Leveraged
ethers-rsfor Ethereum RPC interactions, using directeth_callfor ERC20 balance queries and swap simulations. This approach ensures we're working with real on-chain data without requiring transaction signing for read operations. -
Financial Precision: Used
rust_decimalthroughout the codebase for all financial calculations to avoid floating-point precision errors. All token amounts are handled with proper decimal places based on the token's decimals field. -
Swap Simulation: For swap operations, the implementation constructs real Uniswap V2/V3 transactions and simulates them using
eth_call(viaprovider.call()), which executes the transaction locally without broadcasting it to the network. This provides accurate estimates including gas costs without requiring actual transaction execution. -
Price Data: Integrated CoinGecko API for token price data, supporting both contract addresses and symbol lookups. The implementation handles common token symbols and provides fallback mechanisms for price queries.
-
Ethereum Mainnet Only: The server is currently configured for Ethereum mainnet. Support for testnets (Goerli, Sepolia) would require additional configuration and potentially different Uniswap router addresses.
-
Price API Dependency: Token price queries depend on CoinGecko's free API, which has rate limits. High-frequency queries may hit rate limits, and the API may not support all tokens.
-
Uniswap Router Addresses: The implementation uses hardcoded Uniswap V2 and V3 router addresses for mainnet. Different networks or future router upgrades would require code changes.
-
Gas Estimation: Gas estimates are approximate and based on transaction simulation. Actual gas costs may vary depending on network conditions at execution time.
-
No Wallet Management or Transaction Signing: The server only simulates swaps and does not execute them on-chain. Wallet management, private key handling, and transaction signing functionality are not implemented. The server cannot sign or broadcast transactions to the blockchain.
-
Slippage Calculation: Minimum output calculation uses simple percentage-based slippage. More sophisticated slippage models (e.g., dynamic slippage based on pool liquidity) are not implemented.
-
Price Impact: Price impact calculation is not fully implemented for all swap scenarios, especially for large trades that might affect pool prices.
This project is licensed under the Apache License 2.0.