EchoLedger, inside your AI client.
A Model Context Protocol endpoint exposing eleven live tools over Uniswap V2/V3, Balancer weighted, and Curve stableswap pools — ten reactive analytics primitives plus BuildStateTwin, a portable State Twin you can run counterfactuals against locally. Connect from Claude, Cursor, or any MCP-compatible client, then ask in plain language or call the tools directly — the analytics are exact AMM math, powered by open-source defipy.
https://mcp.echoledger.ai/mcpConnect in one step.
Authless — no account, no API key. You supply an Ethereum RPC URL per call (bring-your-own-RPC); nothing is stored or logged.
Claude Code
claude mcp add --transport http echoledger https://mcp.echoledger.ai/mcpClaude Desktop
Settings → Connectors → Add custom connector, using the endpoint URL above.
Cursor
Settings → MCP → Add new MCP server. Name echoledger, type http, URL the endpoint above.
A first call.
Once connected, ask a natural-language question and the agent picks the tool and fills the arguments — or call a tool directly with an arguments object like this:
Tool call — CheckPoolHealth on a mainnet V3 pool
{
"name": "CheckPoolHealth",
"arguments": {
"pool_address": "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640",
"rpc_url": "https://ethereum-rpc.publicnode.com",
"pool_type": "uniswap_v3"
}
}Response (abridged)
{
"version": "V3",
"spot_price": 0.000414,
"tvl_in_token0": 241360294.59,
"tvl_in_token1": 143584.31,
"num_lps": 1,
"fee_pips": 500,
"tick_current": -74265,
"has_activity": false
}The rpc_urlabove is a public node for trying it out — use your own provider (Alchemy, Infura, etc.) for production. It is read once, server-side, and never persisted.
Common parameters.
Every tool accepts these. Tool-specific parameters are listed under each tool below.
pool_addressstringrequiredOn-chain address of the pool/pair (Uniswap pair, Balancer weighted pool, or Curve stableswap pool). Lowercase, uppercase, or checksum casing all accepted.
rpc_urlstringrequiredAn Ethereum (or L2) JSON-RPC URL used to read live pool state. You supply it per call; it is never stored or logged.
pool_type"uniswap_v2" | "uniswap_v3" | "balancer" | "stableswap"requiredWhich protocol the pool belongs to. Each tool advertises only the values it accepts — the Uniswap tools take uniswap_v2/uniswap_v3, the Balancer tools balancer, the stableswap tools stableswap, and BuildStateTwin all four.
chain_idintegerOptional guard. If supplied and the RPC reports a different chain, the call is rejected.
block_numberintegerOptional. Pin the read to a historical block. Omit to read the latest block.
AnalyzePosition
V2 · V3Decompose an LP position's PnL into impermanent loss, accumulated fees, and net result, measured against the entry basis you provide.
Analyze my position in this Uniswap V3 USDC/ETH pool.
lp_init_amtnumberrequiredLP tokens held by the position, in human units.
entry_x_amtnumberrequiredAmount of token0 deposited at entry.
entry_y_amtnumberrequiredAmount of token1 deposited at entry.
lwr_tickintegerV3 onlyLower tick of the position range. Omit for full range / V2.
upr_tickintegerV3 onlyUpper tick of the position range. Omit for full range / V2.
holding_period_daysnumberOptional. If supplied, real_apr is annualized from net PnL.
net_pnlnumbercurrent_value − hold_value, in token0 numeraire.
il_percentagenumberPure price-divergence impermanent loss (fraction).
fee_incomenumberIsolated fee component, in token0 numeraire.
real_aprnumber | nullAnnualized net return (null unless holding_period_days given).
diagnosisstring"net_positive" | "fee_compensated" | "il_dominant".
SimulatePriceMove
V2 · V3Project a position's value at a hypothetical price change from the current pool state. Size-invariant paper value; fee income is not modeled.
If ETH drops 30%, what happens to my V3 position?
price_change_pctnumberrequiredFractional change from current price; must be > −1.0. e.g. −0.30 = a 30% drop. Or pass the vector form price_change_pcts.
price_change_pctsnumber[]Vector form — sweep a grid of price changes in one call (≤256). Supply this OR the scalar, not both; returns an ordered array of results.
position_size_lpnumberrequiredLP tokens held, in human units; must be > 0.
lwr_tickintegerV3 onlyLower tick of the position range.
upr_tickintegerV3 onlyUpper tick of the position range.
new_valuenumberPosition value at the simulated price (token0 numeraire).
il_at_new_pricenumberImpermanent loss at the simulated price (fraction, ≤ 0).
value_change_pctnumberFractional change in position value from current.
new_price_rationumberAlpha = new_price / current_price.
fee_projectionnullAlways null — fee projection is out of scope for this tool.
CheckPoolHealth
V2 · V3Pool-level snapshot: TVL, reserves, spot price, LP concentration, fee tier, and activity. Answers “would I deposit into this pool?” at the pool level.
Is this V3 pool healthy?
recent_windowintegerV2 onlyRolling window (swap count) for fee_accrual_rate_recent. Default 20; ignored on V3.
tvl_in_token0 / tvl_in_token1numberTotal value locked in each token numeraire.
reserve0 / reserve1numberDecimal-adjusted reserves.
spot_pricenumberCurrent price of token0 in token1 terms.
num_lps / top_lp_share_pctinteger / numberLP count and the top LP's share.
fee_pips / tick_currentinteger | nullV3 onlyFee tier (pips) and current tick; null on V2.
num_swaps / fee_accrual_rate_recentinteger | nullV2 onlySwap count and recent fee rate; null on V3.
DetectRugSignals
V2 · V3Threshold-based rug-pull screen composed over pool health: suspiciously low TVL, top-LP concentration, and inactive-pool-with-liquidity.
Any rug signals on this V2 pair?
lp_concentration_thresholdnumberTop-LP share that trips the concentration flag. (0, 1]; default 0.90; pass 1.0 to disable.
tvl_floornumberMinimum acceptable TVL in token0; values at/below fire the low-TVL flag. Default 10.0 — override per pair.
risk_levelstring"low" | "medium" | "high" | "critical" (count-based bucket).
signals_detectedintegerCount of fired signals (0–3).
tvl_suspiciously_lowbooleanTVL below the floor.
single_sided_concentrationbooleanOne LP owns a dominant share.
inactive_with_liquiditybooleanV2 onlyLiquidity present but no swap activity. Always false on V3.
detailsstring[]Human-readable lines explaining which signals fired.
CalculateSlippage
V2 · V3Slippage and price-impact decomposition for a proposed swap: spot vs execution price, slippage %, cost, and (V2) the max trade within 1% slippage.
How much slippage on a 50,000 USDC buy here?
amount_innumberrequiredAmount of token_in to trade, in human units; must be > 0. Or pass the vector form amounts_in.
amounts_innumber[]Vector form — quote a range of trade sizes in one call (≤256). Supply this OR the scalar, not both; returns an ordered array of results.
token_in_namestringrequiredSymbol of the input token (e.g. "USDC", "WETH") — must match one of the pool's two tokens.
lwr_tickintegerV3 onlyLower tick of the active range.
upr_tickintegerV3 onlyUpper tick of the active range.
slippage_pctnumberSlippage as a fraction of spot.
price_impact_pctnumberPrice impact of the trade.
spot_price / execution_pricenumberPre-trade spot and realized execution price.
slippage_costnumberCost in output-token units.
max_size_at_1pctnumber | nullV2 onlyLargest trade staying within 1% slippage; null on V3.
AnalyzeBalancerLP
BalancerDecompose a 2-asset Balancer weighted-pool position's PnL using the weighted-pool IL formula, where the base token's weight shapes IL magnitude. Fee income is not attributed in v1 (vault-level fees only).
How is my 80/20 BAL/WETH Balancer position doing?
lp_init_amtnumberrequiredPool shares held by this position, in human units.
entry_base_amtnumberrequiredAmount of the base (first) token deposited at entry.
entry_opp_amtnumberrequiredAmount of the opp (second) token deposited at entry.
holding_period_daysnumberOptional. If supplied, real_apr is annualized from net PnL.
net_pnlnumbercurrent_value − hold_value, in opp-token numeraire.
il_percentagenumberWeighted-pool impermanent loss (fraction).
base_weightnumberThe base token's pool weight (e.g. 0.8).
alphanumberCurrent base-in-opp price ratio vs entry.
diagnosisstring"net_positive" | "fee_compensated" | "il_dominant".
SimulateBalancerMove
BalancerProject a 2-asset Balancer position's value at a hypothetical base-token price move from current state. IL depends on both the shock magnitude and the pool weights.
If BAL drops 30%, what happens to my BAL/WETH Balancer LP?
price_change_pctnumberrequiredFractional base-in-opp price change; must be > −1.0. Or pass the vector form price_change_pcts.
price_change_pctsnumber[]Vector form — sweep a grid in one call (≤256). Supply this OR the scalar, not both; returns an ordered array.
lp_init_amtnumberrequiredPool shares held, in human units; must be > 0.
new_valuenumberPosition value at the simulated price (opp numeraire).
il_at_new_pricenumberImpermanent loss at the simulated price (fraction, ≤ 0).
value_change_pctnumberFractional change in position value from current.
new_price_rationumberAlpha = new_price / current_price.
AnalyzeStableswapLP
StableswapDecompose a 2-asset Curve stableswap position's PnL via the amplified-invariant IL formula — small depegs can produce outsized IL at high A. Values in peg-numeraire.
Analyze my position in the crvUSD/USDC pool.
lp_init_amtnumberrequiredLP tokens held, in human units.
entry_amountsnumber[]requiredPer-token entry amounts in pool order — exactly 2 (2-asset pools only).
holding_period_daysnumberOptional. If supplied, real_apr is annualized from net PnL.
il_percentagenumber | nullAmplified-invariant IL (null in the unreachable-alpha regime).
net_pnlnumber | nullcurrent_value − hold_value, in peg-numeraire.
AintegerThe pool's amplification coefficient.
token_namesstring[]Pool tokens in order.
diagnosisstringe.g. "at_peg" | "net_positive" | "il_dominant".
SimulateStableswapMove
StableswapProject a 2-asset stableswap position's value at a hypothetical peg shift. At high A, large shocks may be physically unreachable — those fields return null.
What happens to my USDC/DAI Curve LP if USDC depegs 2%?
price_change_pctnumberrequiredFractional shock to the current alpha; must be > −1.0. Or pass the vector form price_change_pcts.
price_change_pctsnumber[]Vector form — sweep a grid in one call (≤256). Supply this OR the scalar, not both; returns an ordered array.
lp_init_amtnumberrequiredLP tokens held, in human units; must be > 0.
new_valuenumber | nullValue at the simulated peg (null if unreachable at this A).
il_at_new_pricenumber | nullIL at the simulated peg (null if unreachable).
value_change_pctnumber | nullFractional value change (null if unreachable).
new_price_rationumberSimulated alpha = 1 + price_change_pct.
AssessDepegRisk
StableswapImpermanent loss across a ladder of depeg levels for a 2-asset stableswap position, each with an optional constant-product (V2) benchmark.
How exposed is my crvUSD/USDC position to a depeg?
lp_init_amtnumberrequiredLP tokens held, in human units; must be > 0.
depeg_token_namestringSymbol of the asset assumed to depeg. Optional — defaults to the pool's first token.
depeg_levelsnumber[]Depeg magnitudes in (0, 1). Default [0.02, 0.05, 0.10, 0.20, 0.50].
compare_v2booleanIf true (default), each scenario reports the equivalent V2 constant-product IL.
scenariosobject[]Per level: depeg_pct, peg_price, il_pct, lp/hold value, v2_il_comparison.
depeg_tokenstringThe asset assumed to depeg.
current_peg_deviationnumberCurrent deviation from peg.
n_assetsintegerNumber of pool assets (2).
BuildStateTwin
V2 · V3 · Balancer · StableswapRead a pool once and return a portable State Twin — the pool's state as JSON, with a content_hash. A client rehydrates it locally and runs unlimited counterfactuals off the MCP, zero further RPC. Spans all four pool types.
Build me a reusable twin of the USDC/ETH 0.05% pool.
lwr_tickintegerV3 onlyOptional position range, uniswap_v3 only. Omit for the full active-liquidity range; ignored for other pool types.
upr_tickintegerV3 onlyOptional position range, uniswap_v3 only.
__type__stringSnapshot class — V2 / V3 / Balancer / StableswapPoolSnapshot.
(snapshot fields)variouspool_id, protocol, reserves, weights/A, fee/ticks, block_number, chain_id, …
content_hashstring"0x" + sha256 over the body (sort_keys) — recompute to verify integrity.
The four scenario tools (SimulatePriceMove, SimulateBalancerMove, SimulateStableswapMove, CalculateSlippage) also take a vector input to sweep a grid in one call. BuildStateTwinreturns a portable twin you rehydrate locally to run those primitives off the MCP with zero further RPC — build once, run N. Full reference, including the twin round-trip: docs/TOOLS.md.
Things worth knowing.
Bring your own RPC
Any Ethereum or L2 JSON-RPC works (Alchemy, Infura, public nodes). Passed per call, read once server-side, never stored or logged — it is redacted from every receipt.
chain_id guard
Supply chain_idto have the call rejected if your RPC points at a different chain than the pool — catches a wrong-network RPC before it returns junk.
V3 ticks & liquidity
The twin is built from the pool’s full-range active liquidity. lwr_tick/upr_tick describe your positionrange and feed the concentrated-liquidity IL math — they don’t re-scope the pool snapshot.
Read-only
Analytics only — the endpoint never signs or sends transactions. Some fields are protocol-specific (marked V2 only / V3 only above) and return null on the other.
Structured, never silent.
Bad input or an unreachable RPC returns a clear error message (your RPC URL is scrubbed from it), not a crash or empty result.
BadPoolTypepool_type is not one of uniswap_v2 / uniswap_v3 / balancer / stableswap.
IncompatiblePoolTypeThe tool doesn't accept that pool_type (e.g. a stableswap tool on a uniswap_v2 pool) — rejected before any chain read.
MissingArgumentpool_address, rpc_url, or pool_type was omitted.
ChainMismatchDeclared chain_id ≠ the chain the RPC reports.
Error reading poolRPC unreachable, or the address isn't a contract on that chain.
Token not foundtoken_in_name doesn't match either pool token (the error lists the available symbols).
The math is open. The reports are paid.
Each call reads live Uniswap V2/V3 pool state through your RPC, builds a stateless twin, runs a closed-form AMM primitive, and returns a typed result. Nothing is cached between calls.
The analytics are powered by the open-source defipy library and its State Twin substrate — the same methodology behind EchoLedger’s paid reports. The library is open and verifiable; this endpoint is the free, self-serve surface.