@drparadox05/lido-mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example ADDED
@@ -0,0 +1,7 @@
1
+ LIDO_PRIVATE_KEY=0x_your_private_key_here
2
+ ETHEREUM_RPC_URL=https://eth-mainnet.your-rpc.example
3
+ BASE_RPC_URL=https://mainnet.base.org
4
+ OPTIMISM_RPC_URL=https://mainnet.optimism.io
5
+ ARBITRUM_RPC_URL=https://arb1.arbitrum.io/rpc
6
+ UNISWAP_API_KEY=your_uniswap_api_key_here
7
+ UNISWAP_UNIVERSAL_ROUTER_VERSION=2.0
package/DEMO.md ADDED
@@ -0,0 +1,145 @@
1
+ # Demo Guide
2
+
3
+ ## Goal
4
+
5
+ This guide gives a judge or developer a fast path to evaluate the Lido MCP server in Cursor or Claude Desktop.
6
+
7
+ The strongest story is:
8
+
9
+ - inspect setup
10
+ - inspect the full Lido portfolio
11
+ - preflight a write safely
12
+ - dry-run the write
13
+ - preflight a Uniswap bridge or swap route safely
14
+ - dry-run the Uniswap route with approvals surfaced explicitly
15
+ - inspect governance context
16
+
17
+ ## Recommended flow
18
+
19
+ ### 1. Check setup
20
+
21
+ Prompt:
22
+
23
+ ```text
24
+ Show my Lido MCP setup status.
25
+ ```
26
+
27
+ Expected outcome:
28
+
29
+ - confirms wallet configuration
30
+ - confirms RPC availability
31
+ - shows readable and writable networks
32
+
33
+ ### 2. Inspect the full portfolio
34
+
35
+ Prompt:
36
+
37
+ ```text
38
+ Show my aggregated Lido portfolio summary.
39
+ ```
40
+
41
+ Expected outcome:
42
+
43
+ - balances across Ethereum, Base, Optimism, and Arbitrum
44
+ - total stETH-equivalent exposure
45
+ - withdrawal queue status and claimable ETH
46
+ - recent governance context
47
+ - action recommendations
48
+
49
+ ### 3. Preflight a write before execution
50
+
51
+ Prompt:
52
+
53
+ ```text
54
+ Preflight wrapping 0.1 stETH into wstETH on Ethereum.
55
+ ```
56
+
57
+ Expected outcome:
58
+
59
+ - checks wallet presence
60
+ - validates balance
61
+ - checks allowance
62
+ - states whether approval is required
63
+ - recommends the next safe command
64
+
65
+ ### 4. Dry-run the actual write
66
+
67
+ Prompt:
68
+
69
+ ```text
70
+ Dry run wrapping 0.1 stETH into wstETH on Ethereum.
71
+ ```
72
+
73
+ Expected outcome:
74
+
75
+ - returns the simulated request
76
+ - shows whether approval is part of the path
77
+ - does not broadcast a transaction
78
+
79
+ ### 5. Inspect governance
80
+
81
+ Prompt:
82
+
83
+ ```text
84
+ Show the 5 most recent Lido governance proposals and whether my wallet can vote.
85
+ ```
86
+
87
+ Expected outcome:
88
+
89
+ - recent proposals
90
+ - execution state
91
+ - wallet voting eligibility if a wallet is configured
92
+
93
+ ### 6. Preflight a Uniswap bridge or swap route
94
+
95
+ Prompt:
96
+
97
+ ```text
98
+ Preflight a Uniswap route from ETH on Ethereum into wstETH on Base.
99
+ ```
100
+
101
+ Expected outcome:
102
+
103
+ - confirms Uniswap API readiness
104
+ - resolves token aliases and source/destination chains
105
+ - shows whether approval is required on the source chain
106
+ - reports the selected routing type and whether the server can execute it live
107
+ - recommends the next safe command
108
+
109
+ ### 7. Dry-run the Uniswap route
110
+
111
+ Prompt:
112
+
113
+ ```text
114
+ Dry run a Uniswap route from wstETH on Base into native ETH on Base.
115
+ ```
116
+
117
+ Expected outcome:
118
+
119
+ - returns approval transaction details if needed
120
+ - returns the generated swap transaction calldata
121
+ - does not broadcast any transaction
122
+ - makes the route shape and gas expectations explicit before execution
123
+
124
+ ## Copy-paste prompt set
125
+
126
+ ```text
127
+ Show my Lido MCP setup status.
128
+ Show my aggregated Lido portfolio summary.
129
+ Show bridgable destinations for wstETH on Ethereum.
130
+ Preflight a Uniswap route from ETH on Ethereum into wstETH on Base.
131
+ Dry run a Uniswap route from wstETH on Base into native ETH on Base.
132
+ Preflight staking 0.01 ETH on Ethereum.
133
+ Preflight wrapping 0.1 stETH into wstETH on Ethereum.
134
+ Dry run wrapping 0.1 stETH into wstETH on Ethereum.
135
+ Show my withdrawal requests and claimable ETH on Ethereum.
136
+ Show the 5 most recent Lido governance proposals.
137
+ ```
138
+
139
+ ## What judges should notice
140
+
141
+ - all write actions are safe by default because `dry_run=true`
142
+ - the preflight tool turns raw contract actions into agent-friendly safety checks
143
+ - the Uniswap route preflight exposes approval requirements and route compatibility before any source-chain transaction is sent
144
+ - the portfolio summary tool gives a single high-signal view instead of forcing many separate calls
145
+ - Ethereum is the canonical execution layer for Lido core actions while L2s provide read visibility for bridged assets
package/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # Lido MCP Server
2
+
3
+ A reference MCP server for Lido that exposes real on-chain tools for:
4
+
5
+ - staking ETH into `stETH`
6
+ - wrapping and unwrapping `stETH` and `wstETH`
7
+ - requesting and claiming withdrawals through the Lido withdrawal queue
8
+ - balance and reward-aware position queries
9
+ - Lido DAO governance proposal queries and voting
10
+ - Uniswap-powered swaps and cross-chain bridge routing with approval-aware preflight and dry-run execution
11
+ - agent-friendly discovery and mental-model guidance for safe tool selection
12
+
13
+ The server uses direct contract calls through `viem`.
14
+ It is not a REST wrapper.
15
+ All write tools support `dry_run` and default to `true`.
16
+
17
+ ## Installation & usage
18
+
19
+ **For users:** See [USER_GUIDE.md](USER_GUIDE.md) for complete setup instructions.
20
+
21
+ **Quick start:**
22
+
23
+ You don't need to manually install anything. Just add this to your MCP client config (Cursor, Claude Desktop, etc.):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "lido": {
29
+ "command": "npx",
30
+ "args": ["-y", "lido-mcp-server"],
31
+ "env": {
32
+ "LIDO_PRIVATE_KEY": "0x...",
33
+ "ETHEREUM_RPC_URL": "https://eth-mainnet.g.alchemy.com/v2/...",
34
+ "UNISWAP_API_KEY": "..."
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ > [!TIP]
42
+ > **Important:** To get the best out of this server, feed the `lido.skill.md` file to your AI agent (in custom instructions or workspace rules). This ensures the agent understands Lido's rebasing mechanics and safety patterns.
43
+
44
+ **Configuration templates:**
45
+ - [Example Config](mcp.config.example.json) - Standard configuration example
46
+
47
+ ## Project structure
48
+
49
+ ```text
50
+ src/
51
+ config/
52
+ env.ts
53
+ lido/
54
+ abis.ts
55
+ advisor.ts
56
+ account.ts
57
+ clients.ts
58
+ governance.ts
59
+ index.ts
60
+ networks.ts
61
+ rewards.ts
62
+ setup.ts
63
+ staking.ts
64
+ types.ts
65
+ utils.ts
66
+ withdrawals.ts
67
+ server/
68
+ createServer.ts
69
+ index.ts
70
+ shared/
71
+ mcp.ts
72
+ uniswap/
73
+ api.ts
74
+ index.ts
75
+ tokens.ts
76
+ types.ts
77
+ index.ts
78
+ ```
79
+
80
+ The repo is structured so contract metadata, runtime configuration, chain clients, business logic, and MCP transport wiring are separated.
81
+ Sensitive environment access is centralized in `src/config/env.ts` instead of being scattered across the codebase.
82
+
83
+ ## Supported networks
84
+
85
+ - `ethereum`
86
+ - `base`
87
+ - `optimism`
88
+ - `arbitrum`
89
+
90
+ Core staking, withdrawal queue actions, wrapping, and governance execute on `ethereum`.
91
+ L2 networks are supported for balance-aware reads of bridged Lido assets.
92
+
93
+ ## Implemented MCP tools
94
+
95
+ - `lido_get_setup`
96
+ - `lido_get_agent_guide`
97
+ - `lido_get_account_overview`
98
+ - `lido_get_portfolio_summary`
99
+ - `lido_preflight_write_action`
100
+ - `lido_get_rewards`
101
+ - `lido_stake_eth`
102
+ - `lido_wrap_steth`
103
+ - `lido_unwrap_wsteth`
104
+ - `lido_request_unstake`
105
+ - `lido_get_withdrawal_requests`
106
+ - `lido_claim_withdrawals`
107
+ - `lido_get_governance_proposals`
108
+ - `lido_vote_on_proposal`
109
+ - `lido_execute_proposal`
110
+ - `lido_preflight_uniswap_route`
111
+ - `lido_execute_uniswap_route`
112
+ - `lido_get_uniswap_route_status`
113
+ - `lido_get_uniswap_bridgable_tokens`
114
+
115
+ ## Quick start
116
+
117
+ ### 1. Install dependencies
118
+
119
+ ```bash
120
+ npm install
121
+ ```
122
+
123
+ ### 2. Configure environment
124
+
125
+ Copy `.env.example` to `.env` or export variables in your shell.
126
+
127
+ Required for writes:
128
+
129
+ - `LIDO_PRIVATE_KEY`
130
+ - `ETHEREUM_RPC_URL`
131
+
132
+ Useful variables:
133
+
134
+ - `ETHEREUM_RPC_URL`
135
+ - `BASE_RPC_URL`
136
+ - `OPTIMISM_RPC_URL`
137
+ - `ARBITRUM_RPC_URL`
138
+
139
+ Required for Uniswap-powered route discovery and execution:
140
+
141
+ - `UNISWAP_API_KEY`
142
+
143
+ Optional for Uniswap route generation:
144
+
145
+ - `UNISWAP_UNIVERSAL_ROUTER_VERSION`
146
+
147
+ ### 3. Build
148
+
149
+ ```bash
150
+ npm run build
151
+ ```
152
+
153
+ ### 4. Run over stdio
154
+
155
+ ```bash
156
+ npm start
157
+ ```
158
+
159
+ For local development:
160
+
161
+ ```bash
162
+ npm run dev
163
+ ```
164
+
165
+ ## Judge and developer quick prompts
166
+
167
+ Use these prompts directly in Cursor or Claude after connecting the MCP server:
168
+
169
+ ```text
170
+ Show my Lido MCP setup status.
171
+ Show the Lido agent guide for staking.
172
+ Show my aggregated Lido portfolio summary.
173
+ Show bridgable destinations for wstETH on Ethereum.
174
+ Preflight a Uniswap route from ETH on Ethereum into wstETH on Base.
175
+ Dry run a Uniswap route from wstETH on Base into native ETH on Base.
176
+ Preflight staking 0.01 ETH on Ethereum.
177
+ Preflight wrapping 0.1 stETH into wstETH on Ethereum.
178
+ Dry run wrapping 0.1 stETH into wstETH on Ethereum.
179
+ Show my withdrawal requests and claimable ETH on Ethereum.
180
+ Show the 5 most recent Lido governance proposals.
181
+ ```
182
+
183
+ A fuller walkthrough lives in `DEMO.md`.
184
+
185
+ ## Cursor or Claude MCP configuration
186
+
187
+ Use a stdio MCP entry that launches the server from this repo.
188
+
189
+ Example:
190
+
191
+ ```json
192
+ {
193
+ "mcpServers": {
194
+ "lido": {
195
+ "command": "node",
196
+ "args": ["/home/drparadox/synthesis_hack/dist/index.js"],
197
+ "env": {
198
+ "LIDO_PRIVATE_KEY": "0x...",
199
+ "ETHEREUM_RPC_URL": "https://eth-mainnet.your-rpc.example",
200
+ "BASE_RPC_URL": "https://mainnet.base.org",
201
+ "OPTIMISM_RPC_URL": "https://mainnet.optimism.io",
202
+ "ARBITRUM_RPC_URL": "https://arb1.arbitrum.io/rpc",
203
+ "UNISWAP_API_KEY": "your-uniswap-api-key"
204
+ }
205
+ }
206
+ }
207
+ }
208
+ ```
209
+
210
+ If you prefer running TypeScript directly during development, point the MCP client at `tsx` and `/home/drparadox/synthesis_hack/src/index.ts` instead.
211
+
212
+ ## Safety model
213
+
214
+ - All write tools default to `dry_run=true`
215
+ - `lido_get_setup` returns recommended first tools, safety defaults, quickstart prompts, and Uniswap API readiness metadata
216
+ - `lido_get_agent_guide` returns the Lido mental model, network boundaries, workflow guidance, and topic-specific tool recommendations
217
+ - `lido_preflight_write_action` can validate the path before a Lido-core write is even dry-run
218
+ - `lido_preflight_uniswap_route` checks approval requirements, route type, and execution compatibility before a Uniswap swap or bridge is attempted
219
+ - `lido_execute_uniswap_route` defaults to `dry_run=true` and uses a direct approval-then-swap flow with `x-permit2-disabled=true`
220
+ - `lido_get_uniswap_route_status` can track the source-chain transaction status for swap and bridge routes after submission
221
+ - Approval-dependent flows report when an approval is required before the main action can execute
222
+ - Withdrawal requests are treated as queue entries, not instant ETH exits
223
+ - Governance writes require the configured wallet to be eligible to act
224
+
225
+ ## Notes on rewards
226
+
227
+ Lido rewards are not a simple claimable bucket.
228
+
229
+ - `stETH` rewards appear through rebasing balances
230
+ - `wstETH` rewards appear through an increasing conversion rate to `stETH`
231
+
232
+ The `lido_get_rewards` tool can return current reward context or a net on-chain balance delta since a historical block.
233
+ That historical delta should only be interpreted as pure staking rewards when the address had no transfers in or out during the interval.
234
+
235
+ ## Notes on unstaking
236
+
237
+ Unstaking on Lido is a two-phase path:
238
+
239
+ - request withdrawal from `stETH` or `wstETH`
240
+ - wait for queue finalization and then claim ETH
241
+
242
+ The server exposes both phases separately.
243
+
244
+ ## Skill file
245
+
246
+ The repo includes `lido.skill.md`, which gives an agent the correct mental model for:
247
+
248
+ - rebasing `stETH`
249
+ - non-rebasing `wstETH`
250
+ - L2 vs Ethereum responsibilities
251
+ - safe approval and queue usage
252
+ - governance caution
@@ -0,0 +1,53 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ const PRIVATE_KEY_ENV_KEYS = ['LIDO_PRIVATE_KEY', 'WALLET_PRIVATE_KEY', 'PRIVATE_KEY'];
5
+ function stripWrappingQuotes(value) {
6
+ if ((value.startsWith('"') && value.endsWith('"'))
7
+ || (value.startsWith("'") && value.endsWith("'"))) {
8
+ return value.slice(1, -1);
9
+ }
10
+ return value;
11
+ }
12
+ function loadDotEnvFile() {
13
+ const currentDir = dirname(fileURLToPath(import.meta.url));
14
+ const envPath = resolve(currentDir, '../../.env');
15
+ if (!existsSync(envPath)) {
16
+ return;
17
+ }
18
+ const content = readFileSync(envPath, 'utf8');
19
+ for (const rawLine of content.split(/\r?\n/u)) {
20
+ const line = rawLine.trim();
21
+ if (!line || line.startsWith('#')) {
22
+ continue;
23
+ }
24
+ const separatorIndex = line.indexOf('=');
25
+ if (separatorIndex <= 0) {
26
+ continue;
27
+ }
28
+ const key = line.slice(0, separatorIndex).trim();
29
+ const value = stripWrappingQuotes(line.slice(separatorIndex + 1).trim());
30
+ if (!key || process.env[key] !== undefined) {
31
+ continue;
32
+ }
33
+ process.env[key] = value;
34
+ }
35
+ }
36
+ loadDotEnvFile();
37
+ export function getOptionalEnv(name) {
38
+ const value = process.env[name]?.trim();
39
+ return value && value.length > 0 ? value : undefined;
40
+ }
41
+ export function hasPrivateKeyConfigured() {
42
+ return PRIVATE_KEY_ENV_KEYS.some((name) => Boolean(getOptionalEnv(name)));
43
+ }
44
+ export function getPrivateKey() {
45
+ const value = PRIVATE_KEY_ENV_KEYS.map((name) => getOptionalEnv(name)).find((candidate) => Boolean(candidate));
46
+ if (!value) {
47
+ throw new Error(`No private key configured. Set ${PRIVATE_KEY_ENV_KEYS.join(', ')}.`);
48
+ }
49
+ if (!/^0x[0-9a-fA-F]{64}$/.test(value)) {
50
+ throw new Error('Configured private key is not a valid 32-byte hex string.');
51
+ }
52
+ return value;
53
+ }
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
+ import { createServer } from './server/createServer.js';
3
+ async function main() {
4
+ process.stdin.resume();
5
+ const server = createServer();
6
+ const transport = new StdioServerTransport();
7
+ await server.connect(transport);
8
+ }
9
+ main().catch((error) => {
10
+ console.error(error);
11
+ process.exit(1);
12
+ });