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 addressget_token_price- Get current token prices in USD or ETH from CoinGeckoswap_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
-
Clone the repository:
git clone cd mpc -
Install dependencies:
cargo build -
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 functionalityThe
.envfile 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
.envfile is already in.gitignoreand will not be committed to version control. Never commit your private keys! -
Build the project:
cargo build --release -
Run the server:
cargo run --releaseThe server starts an HTTP server on port 3000 (default). You can change the port by setting the
PORTenvironment 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
-
Ethers-rs over Alloy: Chose ethers 1.0 for better stability and ecosystem maturity, despite alloy being newer.
-
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.
-
Decimal Precision: Used
rust_decimalfor all financial calculations to avoid floating-point precision errors when dealing with token amounts and prices. -
Swap Simulation: The
swap_tokenstool constructs real Uniswap V2 swap transactions and useseth_callto simulate them without executing on-chain, providing accurate estimates of output amounts and gas costs. -
Error Handling: Comprehensive error types with
thiserrorfor better error propagation and debugging.
Known Limitations
-
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.
-
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.
-
Token Decimals: Currently assumes 18 decimals for tokens in swap calculations. Production code should fetch decimals from the contract.
-
Price Data: Uses CoinGecko free tier API. Rate limits may apply with high usage.
-
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.
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 →