Lido MCP Server
by the-wunmi
README.md
# Lido MCP Server
The reference MCP server for Lido — stETH staking, position management, governance, and earn vault monitoring, all natively callable by any AI agent.
Point Claude, Cursor, or any MCP-compatible agent at this server and stake ETH from a conversation. No custom integration code needed.
## Why This Exists
AI agents need to interact with DeFi protocols, but bridging natural language to on-chain transactions is hard — private keys, gas estimation, protocol quirks (stETH rebasing), transaction safety. This MCP server gives agents a structured, safe interface to Lido:
- **Every write operation supports dry-run simulation** — agents preview gas costs and verify transactions before executing
- **A mental model document** (`lido.skill.md`) teaches agents Lido concepts before they act — rebasing mechanics, wstETH vs stETH tradeoffs, safe staking patterns
- **Live vault monitoring with intelligent alerts** — watch Lido Earn vaults for yield changes, TVL shifts, share price anomalies, and protocol allocation shifts (Aave, Morpho, Pendle, Gearbox, Maple), with alerts in plain language via Telegram or email
- **Flexible rule engine** — define alert conditions as readable expressions like `apr < 3.0` or `spread_vs_steth < -0.5`, with automatic stETH benchmark comparison
- **AI-powered explanations** — optionally uses Claude to translate raw vault events into plain-language messages explaining what changed, why, and whether you need to act
- **Security guardrails** — `read-only`, `dry-run-only`, and `full` modes, receiver allowlists, per-transaction ETH caps
## MCP Server
**70+ tools** across staking, governance, vault monitoring, and L2 — every write operation defaults to `dry_run: true`.
| Category | Tools |
|----------|-------|
| **Query** | `lido_get_balances`, `lido_get_staking_apr`, `lido_get_rewards`, `lido_get_protocol_status`, `lido_convert_amounts`, `lido_get_withdrawal_requests`, `lido_get_claimable_eth`, `lido_get_chain_info` |
| **Intelligence** | `lido_analyze_position`, `lido_estimate_withdrawal_time`, `lido_check_steth_rate`, `lido_check_gas_conditions`, `lido_get_swap_quote` |
| **Stake / Wrap / Withdraw** | `lido_stake_eth`, `lido_wrap_steth_to_wsteth`, `lido_wrap_eth_to_wsteth`, `lido_unwrap_wsteth_to_steth`, `lido_request_withdrawal`, `lido_claim_withdrawal`, `lido_swap_eth_for_ldo` |
| **Governance** | Dual governance state + lock/unlock stETH in veto escrow, Aragon DAO vote/analyze/decode, Snapshot proposals + voting, Easy Track motions + objections, voting power + veto thresholds + timeline |
| **Earn Vault Monitor** | `lido_list_earn_vaults`, `lido_watch_vault`, `lido_unwatch_vault`, `lido_add_rule`, `lido_remove_rule`, `lido_check_vault`, `lido_list_watches`, `lido_get_vault_alerts`, `lido_test_notifications` |
| **stVaults V3** | List, inspect, fund, withdraw, pause/resume beacon deposits, mint/burn shares, rebalance, create vault, request validator exit |
| **Protocol Info** | TVL, fee structure, staking modules, node operators, contract addresses |
| **Token Management** | Token info, allowances, approve, transfer, revoke for stETH/wstETH/LDO |
| **Withdrawal NFTs** | Owner lookup, transfer, approve |
| **L2 wstETH** | Balance, transfer, info on Base/Optimism/Arbitrum + cross-chain balances across 11 L2s |
| **L2 stETH** | Rebasing stETH balance + transfer on Optimism |
**6 guided prompts** — multi-step workflows for staking (`stake-eth-safely`), position analysis (`manage-position`), withdrawals (`withdraw-steth`), governance review (`review-governance`), vault management (`manage-vault`), and governance participation (`participate-governance`).
**4 live resources** — `lido://position/{address}`, `lido://protocol/status`, `lido://governance/state`, `lido://governance/votes`.
**Agent mental model** (`lido.skill.md`) — see "Why This Exists" above.
### Key Design Decisions
- **Real SDK, no mocks** — all operations go through `@lidofinance/lido-ethereum-sdk` and `viem` to actual Ethereum contracts
- **Dry-run by default** — every state-changing tool defaults to `dry_run: true`. Agents must explicitly set `dry_run: false` to execute
- **Protocol-aware errors** — raw blockchain errors (insufficient funds, reverts, nonce conflicts, stake limits, paused) are translated to human-readable messages
- **MCP annotations** — every tool declares `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint` so agents know which tools are safe to call without confirmation
- **L2 aware** — set `LIDO_CHAIN_ID` to Base, Optimism, or Arbitrum and the server exposes only wstETH-specific tools; L1-only tools are excluded automatically
## Earn Vault Monitor
Watches Lido Earn vaults (EarnETH, EarnUSD) and tells depositors when something worth knowing has changed — in plain language, not raw data.
### How It Works
1. **Watch a vault** — call `lido_watch_vault` with a vault address (use `lido_list_earn_vaults` to discover them)
2. **Define alert rules** — write conditions as readable expressions evaluated against live vault data
3. **Receive alerts** — get notified via Telegram or email when rules fire, with AI-generated explanations
The monitor runs continuously:
- Subscribes to on-chain **`Deposit`/`Withdraw`** events on each vault and **`TokenRebased`** on stETH for near-real-time reaction
- Runs a **30-second polling** health check as a fallback
- Compares vault APR against the **stETH SMA APR** from the Lido API on every check
- **Detects protocol allocation shifts** across underlying protocols (Aave, Morpho, Pendle, Gearbox, Maple) by reading on-chain subvault balances for known Lido Earn vaults
- Persists all state (watches, snapshots, alerts, dedup) in **SQLite** — survives restarts
- **6-hour dedup** window per rule per vault prevents alert fatigue
### Alert Delivery
- **Telegram** — formatted alerts with severity indicators, vault context, benchmark comparison, and actionable guidance
- **Email** — HTML-formatted alerts with the same context
- **AI explanations** (optional) — when `ANTHROPIC_API_KEY` is set, each alert is passed to Claude which generates a plain-language explanation covering:
1. What changed — the specific metric that triggered the alert
2. Why it likely happened — plausible explanations based on the data
3. What to consider — whether the depositor should act, wait, or investigate
### Rule Engine
Rules aren't a fixed set of predefined alerts — they're **open-ended expressions** you define in natural language through the agent. Tell Claude "alert me if yield drops below 3%" and it writes the rule `apr < 3.0`. Say "notify me if the vault starts underperforming stETH by more than half a percent while TVL is also dropping" and it composes `spread_vs_steth < -0.5 and tvl_change_pct < 0`. The agent translates your intent into a mathjs expression evaluated against live vault metrics:
| Variable | What it is |
|----------|------------|
| `apr` / `apy` | Current vault APR (%) |
| `apr_prev` / `apy_prev` | APR from previous snapshot |
| `apr_delta` / `apy_delta` | APR change between snapshots (percentage points) |
| `tvl` | Total value locked |
| `tvl_prev` | TVL from previous snapshot |
| `tvl_change_pct` | TVL change as a percentage |
| `share_price` | Current share price (asset/share) |
| `share_price_prev` | Share price from previous snapshot |
| `share_price_change_pct` | Share price change as a percentage |
| `steth_apr` | stETH SMA APR benchmark (from Lido API) |
| `spread_vs_steth` | Vault APR minus stETH APR (positive = outperforming) |
| `max_alloc_shift` | Largest protocol allocation change between snapshots (pp) |
| `num_protocols` | Number of protocols with active allocations |
| `top_alloc_pct` | Largest single protocol allocation (%) |
**Example rules:**
| Expression | Fires when |
|------------|-----------|
| `apr < 3.0` | Yield drops below 3% — a simple yield floor |
| `spread_vs_steth < 0` | Vault underperforms raw stETH staking |
| `spread_vs_steth < -0.5` | Vault underperforms stETH by more than 0.5pp |
| `tvl_change_pct < -10` | TVL drops >10% between checks — capital flight |
| `share_price_change_pct < -0.1` | Share price drop — possible exploit or depeg |
| `apr < 3.0 and tvl_change_pct < -5` | Yield dropped AND capital leaving |
| `abs(apr_delta) > 2.0` | Large APR swing in either direction |
| `apr < steth_apr - 1.0` | Falls more than 1pp behind stETH benchmark |
| `apr > apr_prev * 1.5` | APR spiked to 1.5x its previous value |
| `max_alloc_shift > 10` | Protocol allocation shifted by more than 10pp |
| `top_alloc_pct > 80` | Single protocol holds >80% of vault capital |
Supports `and`/`or`/`not` boolean logic, comparison operators (`<`, `>`, `<=`, `>=`, `==`, `!=`), arithmetic (`+`, `-`, `*`, `/`), and safe math functions (`abs`, `min`, `max`, `round`, `floor`, `ceil`, `sqrt`).
Each rule includes a **message template** with `{{variable}}` interpolation:
```
APR dropped to {{apr}}%, below your 3% floor. stETH is at {{steth_apr}}%.
```
If no message is provided, one is auto-generated from the expression pattern.
The rule engine is **sandboxed** — expressions are parsed into an AST and validated against an allowlist of variables, functions, and node types. No arbitrary code execution.
### Protocol Allocation Detection
For known Lido Earn vaults (strETH/EarnETH), the monitor reads on-chain balances across each vault subvault to determine how capital is distributed across underlying protocols — Aave, Morpho, Pendle, Gearbox, Maple, and others. Each health check compares the current allocation to the previous snapshot and detects shifts.
Allocation data appears in:
- **Health reports** (`lido_check_vault`) — shows per-protocol percentage breakdown
- **Alert context** — when any rule fires, the alert includes allocation shift details (protocol name, from/to percentages, delta)
- **AI explanations** — the LLM receives allocation shift data and can explain rebalancing events in plain language
- **Rule variables** — `max_alloc_shift`, `num_protocols`, and `top_alloc_pct` let you write rules like `max_alloc_shift > 10` (alert when any protocol's share shifts by more than 10pp)
### MCP-Callable Vault Health
Other agents can query vault health programmatically — the monitor isn't just a notification service, it's a building block:
- **`lido_check_vault`** — on-demand health check for any ERC-4626 vault (no watch required). Returns APR, TVL, share price, and stETH benchmark spread
- **`lido_list_earn_vaults`** — discover available Mellow earn vaults from the live API
- **`lido_get_vault_alerts`** — query alert history programmatically
## Target Use Cases
### 1. Developer stakes ETH via Claude — zero integration code
```
User: "Stake 1 ETH with Lido"
Claude: Let me walk you through this safely.
→ Checks protocol status (not paused, limits ok)
→ Checks your balance (1.5 ETH available)
→ Shows current APR (3.4%, 7-day SMA 3.3%)
→ Dry-runs the transaction (gas: 0.002 ETH)
→ "Ready to stake 1 ETH. Gas cost ~0.002 ETH. Confirm?"
User: "Yes"
Claude: → Executes stake
→ "Done. TX: 0xabc... You received 1.0 stETH."
```
### 2. Depositor gets a Telegram message explaining why EarnETH yield dropped overnight
```
User: "Watch the EarnETH vault. Alert me if yield drops below 4%
or it falls behind raw stETH staking."
Claude: → Discovers vault via lido_list_earn_vaults
→ Calls lido_watch_vault with rules:
- apr < 4.0 [warning]
- spread_vs_steth < 0 [critical]
→ "Watching EarnETH (0x...). APR: 5.2%, TVL: 12,450 ETH.
You'll get a Telegram alert if APR drops below 4%
or the vault underperforms stETH."
[Overnight, APR drops to 3.6%]
Telegram: 🚨 EarnETH — Warning
APR dropped to *3.60%*, down from 5.20%.
The vault's yield fell 1.6pp since the last check. This likely
reflects a rotation in the underlying strategy or temporary
market conditions. The vault is still outperforming direct stETH
staking (3.40%) by 0.20pp, so no immediate action is needed —
but worth monitoring if the trend continues.
• Current APR: 3.60%
• Previous APR: 5.20%
• stETH benchmark: 3.40%
• Spread vs stETH: 0.20pp above
View on Etherscan
```
### 3. Agent queries vault health before deciding to deposit
```
Agent: → Calls lido_check_vault(address="0x...")
Response:
=== Vault Health: EarnETH ===
TVL: 12,450.00 ETH
Share Price: 1.034521 ETH/share
APR: 5.20%
--- Benchmark ---
stETH APR (SMA): 3.40%
Spread: 1.80pp above stETH benchmark
--- Protocol Allocations ---
Aave: 32.1%
Morpho: 28.4%
Pendle: 18.7%
Gearbox: 12.3%
Maple: 8.5%
Last checked: 2025-03-23T14:30:00.000Z
Agent: → Diversified allocation, positive spread — decides to deposit
```
### 4. Natural-language rule configuration
```
User: "I want to know if the vault starts doing worse than just
holding stETH, especially if people are also pulling money out.
And give me a heads up if the share price ever drops suddenly —
that could mean something bad happened."
Claude: I'll set up three rules for you.
→ Calls lido_add_rule: "spread_vs_steth < 0 and tvl_change_pct < -5" [warning]
Message: "Vault underperforming stETH (spread {{spread_vs_steth}}pp)
while TVL dropped {{tvl_change_pct}}%"
→ Calls lido_add_rule: "share_price_change_pct < -0.1" [critical]
Message: "Share price dropped {{share_price_change_pct}}% — investigate immediately"
→ Calls lido_add_rule: "spread_vs_steth < -1.0" [critical]
Message: "Vault is {{spread_vs_steth}}pp behind stETH — significant underperformance"
"Done. Three rules active:
1. Warning if the vault trails stETH while TVL is declining
2. Critical alert on any sudden share price drop (>0.1%)
3. Critical if the vault falls more than 1pp behind stETH
Dry-ran all three against current data — none would fire right now
(APR 5.2% vs stETH 3.4%, TVL stable). You'll get alerts the
moment any condition is met."
```
### 5. DAO contributor queries and votes on governance proposals
```
User: "Show me the latest Lido DAO votes"
Claude: → Calls lido_get_aragon_vote
→ "Vote #185: Open — Yea 15.2M LDO (92%), Nay 1.3M LDO (8%)
Vote #184: Executed — passed with 98% support ..."
User: "Vote yes on #185"
Claude: → Calls lido_vote_on_proposal (dry_run=true)
→ "Dry run: Vote Yea on #185. You have 5,000 LDO. Gas: ~0.001 ETH. Confirm?"
User: "Yes"
Claude: → Executes vote
→ "Done. Voted Yea on #185. TX: 0xabc..."
```
### 6. Staker signals opposition via Dual Governance
```
User: "I want to signal opposition to the latest proposal."
Claude: → Calls lido_get_governance_state
→ "Governance is in Normal state. Veto signalling is at 0.12%,
well below the first seal threshold of 1%."
→ Dry-runs lock of 10 stETH in escrow
→ "Ready to lock 10 stETH for governance. Gas: ~0.003 ETH. Confirm?"
User: "Yes"
Claude: → Approves stETH for escrow + locks
→ "Done. 10 stETH locked in veto signalling escrow. TX: 0xdef..."
```
## Setup
```bash
git clone <repo-url>
cd lido-mcp-server
cp .env.example .env # edit with your RPC URL, private key, chain ID
npm install && npm run build && npm start
```
See `.env.example` for all configuration options (chain ID, security mode, Telegram, SMTP, Anthropic).
### Connect to Claude / Cursor
```json
{
"mcpServers": {
"lido": {
"command": "bash",
"args": ["/path/to/lido-mcp-server/start.sh"],
"env": {
"LIDO_RPC_URL": "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
"LIDO_PRIVATE_KEY": "0xYOUR_PRIVATE_KEY",
"LIDO_CHAIN_ID": "1"
}
}
}
}
```
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues