An MCP server for managing hundreds of Ethereum EOA wallets derived from a single BIP44 HD seed. Exposes wallet operations — ETH transfers, ERC20 transfers, arbitrary contract calls — as MCP tools over HTTP. Wallet addresses and balances are cached in a local SQLite database.
Each wallet maps to a BIP44 derivation path index:
wallet-0042 → index 42 → m/44'/60'/0'/0/42
Names are zero-padded to four digits. Any tool that accepts a wallet_name also accepts a bare integer ("42") or an unpadded name ("wallet-42").
No install required — run directly from PyPI with uvx:
{
"mcpServers": {
"beehive": {
"command": "uvx",
"args": ["mcp-beehive"],
"env": {
"MNEMONIC": "your twelve word seed phrase here",
"RPC_URL": "https://mainnet.infura.io/v3/YOUR_KEY"
}
}
}
}Add this to:
- Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json - Claude Code:
.claude/settings.jsonin your project or~/.claude/settings.jsonglobally
- Python 3.12+
- uv
All configuration is via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
MNEMONIC |
Yes (for signing) | — | BIP39 mnemonic phrase |
RPC_URL |
No | https://eth.llamarpc.com |
Default JSON-RPC endpoint |
DB_PATH |
No | beehive.db |
SQLite cache file path |
TRANSPORT |
No | stdio |
stdio or http |
HOST |
No | 0.0.0.0 |
HTTP bind address (http mode only) |
PORT |
No | 8000 |
HTTP port (http mode only) |
Security: keep your mnemonic in a secrets manager or
.envfile that is never committed. The server never returns or logs the mnemonic.
To run as a persistent HTTP server instead:
TRANSPORT=http \
MNEMONIC="your twelve word seed phrase here" \
RPC_URL="https://mainnet.infura.io/v3/YOUR_KEY" \
uvx mcp-beehiveThe MCP endpoint is available at http://localhost:8000/mcp.
Connect Claude to it:
{
"mcpServers": {
"beehive": {
"type": "http",
"url": "http://localhost:8000/mcp"
}
}
}git clone https://github.com/layer-3/mcp-beehive
cd mcp-beehive
uv sync
MNEMONIC="..." uv run python main.py| Tool | Parameters | Description |
|---|---|---|
list_wallets |
start=0, count=10 |
List wallet names and addresses. Max 100 per call. |
get_wallet |
wallet_name |
Address and all cached balances for one wallet. |
| Tool | Parameters | Description |
|---|---|---|
get_eth_balance |
wallet_name, rpc_url?, use_cache? |
ETH balance. Pass use_cache=true to skip an RPC call. |
refresh_eth_balances |
wallet_names[], rpc_url? |
Batch-refresh ETH balances for a list of wallets. |
get_erc20_balance |
wallet_name, token_address, rpc_url? |
ERC20 balance (fetches decimals and symbol automatically). |
| Tool | Parameters | Description |
|---|---|---|
send_eth |
from_wallet, to_address, amount_eth, rpc_url?, fee params |
Send ETH. |
send_erc20 |
from_wallet, token_address, to_address, amount, decimals?, rpc_url?, fee params |
Send ERC20 tokens. Human-readable amount (e.g. "100.5" for 100.5 USDC). |
send_transaction |
from_wallet, to_address, data?, value_eth?, gas?, rpc_url?, fee params |
Arbitrary contract call or deployment. |
get_transaction_status |
tx_hash, rpc_url? |
Check if a transaction is pending, confirmed, or failed. Updates the local cache. |
list_transactions |
wallet_name?, limit=20 |
Recent transactions from the local SQLite cache. |
| Tool | Parameters | Description |
|---|---|---|
get_nonce |
wallet_name, rpc_url? |
Confirmed and pending nonce for a wallet. |
estimate_gas |
from_wallet, to_address, data?, value_eth?, rpc_url? |
Gas estimate and cost in ETH. |
send_eth, send_erc20, and send_transaction all accept optional fee overrides:
| Parameter | Description |
|---|---|
max_fee_gwei |
EIP-1559 max fee per gas (Gwei) |
priority_fee_gwei |
EIP-1559 miner tip (Gwei). Default: 1 |
gas_price_gwei |
Legacy gas price (Gwei). Use on non-EIP-1559 chains. Takes priority over EIP-1559 params. |
If no fee params are provided the server auto-detects the chain type and sets fees from the latest block.
list_wallets(start=0, count=10)
get_eth_balance("wallet-0042")
get_erc20_balance("wallet-0042", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") # USDC
send_eth(
from_wallet="wallet-0042",
to_address="0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
amount_eth="0.1"
)
send_erc20(
from_wallet="wallet-0042",
token_address="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
to_address="0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
amount="50"
)
send_transaction(
from_wallet="wallet-0000",
to_address="0xContractAddress",
data="0xa9059cbb...",
value_eth="0"
)
get_transaction_status("0xabc123...")
Pass rpc_url to any tool to target a different chain. The server detects EIP-1559 support automatically.
send_eth(
from_wallet="wallet-0000",
to_address="0x...",
amount_eth="0.01",
rpc_url="https://polygon-rpc.com"
)
Beehive keeps a local beehive.db (path configurable via DB_PATH) with three tables:
wallets— derived addresses and cached ETH balanceserc20_balances— per-wallet ERC20 balances with symbol and decimalstransactions— submitted transactions and their status
The cache is write-through: balances are updated on every live fetch and transaction status is updated whenever get_transaction_status is called. Cached values are never used for signing — nonces are always fetched fresh from the chain.
uv sync --group dev
uv run pytestTests run entirely offline. Unit tests use an in-memory SQLite database; integration tests inject a mock Web3 instance so no RPC calls are made.
beehive/
├── config.py # env vars, ERC20 ABI
├── wallet.py # pure BIP44 derivation (no I/O)
├── db.py # SQLite CRUD (connection injected)
├── chain.py # Web3 helpers (Web3 instance injected)
├── server.py # FastMCP instance
└── tools/
├── wallets.py
├── balances.py
├── transactions.py
└── utils.py
tests/
├── conftest.py # per-test isolated SQLite
├── unit/ # wallet, db, chain — no network
└── integration/ # tool functions with mocked RPC
main.py # entry point
MIT