Skip to main content
Glama
README.md
# Graph Uniswap MCP

[![npm version](https://img.shields.io/npm/v/graph-uniswap-mcp)](https://www.npmjs.com/package/graph-uniswap-mcp)
[![npm downloads](https://img.shields.io/npm/dm/graph-uniswap-mcp)](https://www.npmjs.com/package/graph-uniswap-mcp)
[![license](https://img.shields.io/npm/l/graph-uniswap-mcp)](LICENSE)
[![node](https://img.shields.io/node/v/graph-uniswap-mcp)](https://nodejs.org)

**Simulate a Uniswap swap before you make it — with no RPC, no node, and no key beyond a free Graph API key.**

One MCP interface over **Uniswap V2, V3 and V4** across **Ethereum, Arbitrum, Base, Polygon, Optimism and BSC**, powered by [The Graph](https://thegraph.com).

```
quote_swap  pool=0x8ad5…e6d8  tokenIn=WETH  amountIn=100

{ "quotable": true,  "amount_out": 223661.14,  "token_out": "USDC",
  "effective_price": 2236.61,  "spot_price": 2250.80,
  "price_impact_pct": 0.6291,  "fee_pct": 0.3,  "ticks_crossed": 2 }
```

## Why this exists

Most Uniswap tooling can tell an agent what a pool *is*. Very little can tell it what a trade
would actually *cost* — and that is the only question that precedes moving money.

Uniswap's own [routing-api](https://github.com/uniswap/routing-api) answers it, but it is
self-host-only: an AWS CDK deployment plus RPC provider keys. `@uniswap/v3-sdk` answers it too,
but it drags ~300MB of Solidity build artifacts (hardhat → `@nomicfoundation/edr-*` prebuilt
binaries) into `node_modules`, which is a poor trade for a server launched with `npx`.

Everything the math needs — `sqrtPrice`, `tick`, `liquidity`, `feeTier`, and the initialised
`ticks` — is already in the subgraph. So this server fetches that and runs the protocol's own
concentrated-liquidity math locally, in ~200 lines of BigInt with **zero runtime dependencies**
beyond the MCP SDK and zod.

**We do not ask you to trust that.** `test/fixtures/v3-quote-oracle.json` holds real mainnet pool
state plus expected outputs *generated by `@uniswap/v3-sdk`*, and `npm test` asserts every case
matches **to the wei** — including trades that cross 26 initialised ticks. Not "close". Identical.
Rounding direction is exactly where a reimplementation of this math goes wrong, so the test
tolerates none.

## What it refuses to do

The failure that matters for a pre-trade tool is not an error — it is a confident wrong number,
because the caller acts on it and never finds out. So `quote_swap` returns `quotable: false` with
a reason instead of a figure when it cannot be trusted:

| Situation | Why it refuses |
|---|---|
| **V4 pool with a hook** | Hooks can override the fee and the pricing curve, so simulating the vanilla curve would be wrong. On Base the top V4 pools by volume are hook-driven with zero in-range liquidity — this is the common case, not an edge case. |
| **Trade larger than the liquidity in view** | The tick window is ±20,000 ticks. A trade that consumes all of it gets a `lower_bound_amount_out`, explicitly not a quote. |
| **Zero in-range liquidity** | Nothing to swap against at the current tick. |
| **No `ticks` entity on the deployment** | The liquidity curve is unknown. Every mapped market now exposes ticks except V4 on Base. |

## TVL is reported as `null`, deliberately

Uniswap V3/V4 native subgraphs accumulate `totalValueLockedUSD` from per-event deltas, and it
drifts. Measured 2026-08-19 on the canonical USDC/WETH 0.3% pool:

| Source | USDC | WETH |
|---|---|---|
| On-chain balance | 6,110,083 | 5,337 |
| Native V3 subgraph | **138,939,548** | **61,693** |

That is 22.7x high on a blue-chip pool, so "spam-token pools inflate TVL" — the usual explanation,
and the one earlier versions of this README gave — does not account for it. Prior versions shipped
that number with a caveat string attached. Agents read fields, not prose, so the caveat did nothing.

`tvl_usd` is now `null` on V3/V4 paths. V2 `reserve0`/`reserve1` are real token balances and are
still returned. **For actual depth, use `quote_swap`** — simulating the trade is a truthful answer
to "how deep is this pool", and a fabricated scalar is not.

## Tools

| Tool | What it does |
|---|---|
| **`quote_swap`** | **Simulate an exact-input swap: amount out, effective price, price impact, ticks crossed** |
| `list_markets` | Every version×chain and its backing subgraph |
| `discover_markets` | Re-resolve live subgraphs; with `apply:true`, replace a market **only if it is broken** |
| `get_token_price` | USD price of a token (symbol or address) |
| `top_pools` | Top pools on a chain, ranked by volume |
| `find_pool` | Pool(s) for a token pair, either order |
| `pool_info` | Full stats for one pool/pair |
| `recent_swaps` | Newest swaps, optionally scoped to a pool |
| `raw_query` | Arbitrary GraphQL against the resolved subgraph |

All tools except `list_markets` take `chain` plus an optional `version` (`v2`/`v3`/`v4`). Chain
aliases (`eth`, `arb`, `matic`, `bnb`, …) are accepted.

### Coverage, precisely

| Chain | V2 | V3 | V4 | `quote_swap` |
|---|---|---|---|---|
| Ethereum | ✅ | ✅ **default** | ✅ pin `v4` | V3 + V4 |
| Arbitrum | — | ✅ **default** | ✅ pin `v4` | V3 |
| Base | ✅ | ✅ **default** | ⚠️ pin `v4` | V3 |
| Polygon | — | ✅ **default** | ✅ pin `v4` | V3 + V4 |
| Optimism | — | ✅ **default** | ✅ pin `v4` | V3 |
| BSC | — | ✅ **default** | ✅ pin `v4` | V3 |

Omit `version` and you get **V3** wherever it exists. Earlier versions ranked by query traffic,
which picked the *most queried* market rather than the most useful one: `chain:"base"` landed on
V4, whose top pools by volume are zero-liquidity hook pools reporting billions in fabricated
volume. Pinning a version always overrides this.

`⚠️` Base V4 is servable when pinned, but is not chosen for you: its top pools by volume are
hook pools with zero in-range liquidity. Uniswap V4 on **Ethereum** is the opposite — real
hookless pairs (USDC/USDT, WBTC/cbBTC, ETH/USDC) with a `ticks` entity, so `quote_swap` works
there too.

Pool field names are **detected, not assumed**: V3 uses `feeTier`/`sqrtPrice`/`feesUSD` while
uniswap-v4-ethereum uses `fee`/`sqrtPriceX96`/`totalFeesUSD` and exposes `tickSpacing`. A new
deployment with either convention works without a code change.

Ranking is always by `volumeUSD`, never TVL — a single spam-token pool can claim trillions in fake
liquidity, so a TVL sort returns junk at the top. This holds even on subgraphs whose per-pool TVL
is accurate.

### discover_markets only heals what is broken

`apply: true` used to install the top GRT-signal name match unconditionally, which could displace
Uniswap Labs' canonical deployment with a stranger and break the market for the rest of the
session — invisibly, since `list_markets` still showed the old id. It now probes the incumbent
first, keeps it if healthy, and probes any replacement before installing it. Candidates are always
listed in `live_signal_candidates` whether or not anything is applied.

## Setup

**1. Get a free Graph API key** at <https://thegraph.com/studio> (Billing → API Keys — first
100k queries/month are free).

**2. Add the server to your MCP client.** No install needed — `npx` fetches the published build.

**Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json`
(Windows: `%APPDATA%\Claude\claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "uniswap": {
      "command": "npx",
      "args": ["-y", "graph-uniswap-mcp"],
      "env": { "GRAPH_API_KEY": "your_key_here" }
    }
  }
}
```

**Claude Code:**

```bash
claude mcp add uniswap -e GRAPH_API_KEY=your_key_here -- npx -y graph-uniswap-mcp
```

## Try it

> "What would selling 100 WETH into the USDC/WETH 0.3% pool on Ethereum actually get me?"

> "Compare price impact for a 50 ETH sell across the 0.05% and 0.3% pools."

> "Find the USDC/WETH pools on Base and show me recent swaps."

## Accuracy envelope

Be precise about what the number means:

- The simulation runs against the subgraph's **most recently indexed block**, not the pending
  state. Real execution depends on the state at inclusion — MEV, pending swaps and indexer lag all
  move it.
- It is a **single-pool** simulation. It does not split across pools or hops; it is not a router.
- Exact-input only.
- V2 uses constant-product math (exact). V3/V4 use tick-walking math, wei-exact versus
  `@uniswap/v3-sdk` on the pinned oracle.

## Development

```bash
npm ci
npm run build
npm test          # offline gate: MCP handshake + tool registry + wei-exact swap math
npm run test:live # deeper checks against live subgraphs (needs GRAPH_API_KEY)
```

To regenerate the oracle fixture (the only thing that needs the Uniswap SDK installed):

```bash
npm i --no-save @uniswap/v3-sdk @uniswap/sdk-core
node scripts/gen-oracle.mjs
```

## License

MIT

TDQS

A3.9/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a distinct purpose: market listing, pool details, swaps, custom queries, market discovery, token price, top pools, and pool finding. No two tools have overlapping functionality, ensuring clear selection.

Naming Consistency4/5

All tool names use snake_case, but the verb + noun pattern is not uniform. For example, 'list_markets' and 'get_token_price' are verb-oriented, while 'pool_info' and 'top_pools' are noun-oriented. This minor inconsistency slightly reduces clarity.

Tool Count5/5

With 8 tools, the server is well-scoped for querying Uniswap data. Each tool adds value without redundancy, covering market discovery, pool analytics, swaps, and token pricing. The count feels natural for the domain.

Completeness4/5

The tool set covers core Uniswap operations: listing markets, pool details, swaps, token price, top pools, and pool finding. Missing are token metadata (e.g., symbol, decimals) and historical data beyond recent swaps. The 'raw_query' escape hatch partially compensates.

Maintenance

ActivityMaintained
ResponsivenessNo issues