Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/mcp-lens-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'lens': minor
---

Add MCP tools `lens_list_pairs`, `lens_get_pools` and `lens_screen_pairs` (#409). Each tool calls the same
implementation as its REST route — `/pairs`, `/pools` and `/screener` now share `listPairs`, `listPools` and
`screenPairs` — so answers cannot drift between transports. Results are bounded by a `limit` (max 100, default
50/50/20) and carry `nextCursor`/`hasMore` for the next page. Assets in tool results always include the full
issuer, never a shortened form. Tool descriptions state when to use each one.
13 changes: 2 additions & 11 deletions src/__tests__/pools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@ vi.mock('../db', () => ({
pgPool: { query: mockQuery },
}))

vi.mock('../redis', () => ({
getCachedPrice: vi.fn(),
setCachedPrice: vi.fn(),
}))

vi.mock('../aggregator/bestRoute', () => ({
getBestRoute: vi.fn(),
}))

import { registerRESTRoutes } from '../api/rest'
import { registerPoolsRoutes } from '../routes/pools'
import { activeNetwork } from '../config'

async function buildApp(network?: 'testnet' | 'mainnet') {
Expand All @@ -27,7 +18,7 @@ async function buildApp(network?: 'testnet' | 'mainnet') {
req.network = network
})
}
await registerRESTRoutes(app)
await registerPoolsRoutes(app)
await app.ready()
return app
}
Expand Down
36 changes: 0 additions & 36 deletions src/api/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
priceResponseSchema,
routeResponseSchema,
historyResponseSchema,
poolsResponseSchema,
depthResponseSchema,
installResponseValidation,
} from './schemas'
Expand Down Expand Up @@ -219,41 +218,6 @@ export async function registerRESTRoutes(app: FastifyInstance) {
}
)

// GET /pools
//
// Latest snapshot per pool for one network, without reading history. The pool
// ids are discovered with a recursive "skip scan" over the
// (network, pool_id, timestamp DESC) index (jump to the next distinct pool_id
// each step), then one LIMIT 1 index probe per pool fetches its newest row.
// Cost is O(pools x log n); the old DISTINCT ON sorted every row of every
// network. A quiet pool is kept with its real `timestamp`, never dropped.
app.get('/pools', { schema: { response: { 200: poolsResponseSchema } } }, async (req) => {
const network = req.network ?? activeNetwork
const result = await pgPool.query(
`WITH RECURSIVE ids AS (
(SELECT pool_id FROM pool_snapshots WHERE network = $1 ORDER BY pool_id LIMIT 1)
UNION ALL
SELECT (SELECT s.pool_id FROM pool_snapshots s
WHERE s.network = $1 AND s.pool_id > ids.pool_id
ORDER BY s.pool_id LIMIT 1)
FROM ids
WHERE ids.pool_id IS NOT NULL
)
SELECT l.pool_id, l.asset_a, l.asset_b,
l.reserve_a::float, l.reserve_b::float, l.spot_price::float, l.fee_bp, l.timestamp
FROM ids
CROSS JOIN LATERAL (
SELECT * FROM pool_snapshots s
WHERE s.network = $1 AND s.pool_id = ids.pool_id
ORDER BY s.timestamp DESC
LIMIT 1
) l
WHERE ids.pool_id IS NOT NULL`,
[network]
)
return { pools: result.rows }
})

// GET /price/:assetA/:assetB/depth?amount=1000
app.get<{
Params: { assetA: string; assetB: string }
Expand Down
2 changes: 2 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { registerGraphQL } from './api/graphql'
import { registerWebhookRoutes } from './routes/webhooks'
import { registerCandleRoutes } from './routes/candles'
import { registerPairsRoutes } from './routes/pairs'
import { registerPoolsRoutes } from './routes/pools'
import { registerScreenerRoutes } from './routes/screener'
import { registerHistoryRoutes } from './api/history'
import { registerX402 } from './middleware/x402'
Expand Down Expand Up @@ -168,6 +169,7 @@ export async function buildApp(options: BuildAppOptions = {}): Promise<FastifyIn
await registerWebhookRoutes(app)
await registerCandleRoutes(app)
await registerPairsRoutes(app)
await registerPoolsRoutes(app)
await registerScreenerRoutes(app)
await registerHistoryRoutes(app)
await registerPriceRoutes(app)
Expand Down
Loading
Loading