Repository navigation
Conversation
…bs#409) Agents could ask for a price but had no way to discover what is quotable, what liquidity sits behind a pair, or what is moving — REST answered all three while MCP answered none, leaving agents to guess asset codes. lens_list_pairs, lens_get_pools and lens_screen_pairs call the same listPairs/listPools/screenPairs the REST routes now delegate to, so the two transports cannot drift apart. Each result is bounded by a limit (max 100) and carries nextCursor/hasMore, assets always include the full issuer, and the tool descriptions spell out which question each one answers.
|
@Ugasutun Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Coordination note — ten PRs are editing one 179-line fileIf your MCP PR has just gone red with a conflict, this is why, and it is my fault rather than yours. I published 22 MCP issues (#404–#425) as if they were independent. They are not: almost all of them land in Already merged:
Order I intend to take the rest in, so you can rebase once rather than repeatedly:
Please do not all rebase at once. If you are below the one currently in flight, wait until the one above you lands — otherwise you rebase, I merge someone else, and you rebase again. I will comment on your PR when it is your turn. One thing to settle between you: Nothing here reflects on the quality of the work. Several of these are among the best PRs this repo has had. |
tsc --noEmit clean vitest 97 files, 1127 passing (up from 93 / 1068) dropped-tests none #325 GET /movers for market gainers and losers #327 GET /correlation for basket construction #485 net arbitrage spread against fees #494 declare the resources capability and expose pages over MCP Two fixes on merge. #325 shipped the route file and the OpenAPI entry but never registered it. The repo's own guard caught it: AssertionError: Documented but unregistered: ["GET /movers"] So the endpoint was documented, generated into clients, and did not exist. registerMoversRoutes already followed the house signature, so wiring it was an import and a call next to registerScreenerRoutes. That guard is worth its weight -- a documented route that 404s is worse than an undocumented one, because a client is generated against it. #327 added a console.error to src/routes/correlation.ts, which the logging hygiene test forbids. Now createLogger, the same change #388 and #318 needed in the previous batch. Three PRs in two batches have tripped that test, which suggests the rule is not discoverable enough from the outside -- worth a line in CONTRIBUTING rather than catching it per-PR. NOT merged, each failing on its own branch rather than from merge order: #460 Phoenix AMM adapter -- 5 failures alone, including its own networkVenueConfig and venues/registry tests #353 1 tsc error #354 1 tsc error #482 4 tsc errors #393 #431 #434 #472 conflict Claude-Session: https://claude.ai/code/session_01USgemLt4Rnz4SGB1Srf3GB
Miracle656
left a comment
There was a problem hiding this comment.
Screened against current main and this does not pass on its own branch, so a rebase alone will not fix it.
Merged alone it gives type errors against main. Run npx tsc --noEmit on a fresh rebase and you will see them.
Please rebase, get npx tsc --noEmit and npx vitest run both clean locally, and re-request. I would rather take this than not — it is held on correctness, not on wanting it.
Miracle656
left a comment
There was a problem hiding this comment.
The design is right and I want this in. It needs a rebase, because the screener was extracted on main in the week since you branched — differently from how you extracted it here, and in the same file.
The principle is the one I'd have asked for
* `lens_screen_pairs` — over the exact implementations the REST routes use
* (`listPairs`, `listPools`, `screenPairs`), so a page from a tool is a page
That is exactly right, and it is the thing MCP tool PRs usually get wrong: re-querying the database from the tool layer, so lens_screen_pairs and GET /screener slowly stop agreeing about what sortBy=liquidity or limit=500 means. Routing the tools through the same functions means there is one answer and one place to fix it.
Pulling /pools out of src/api/rest.ts into src/routes/pools.ts is also a genuine improvement independent of the tools — rest.ts has been a grab-bag for a while.
What changed under you
main now has src/services/screener.ts, exporting runScreener. It arrived with the GraphQL screener query (#609), which needed the same thing you need: one implementation behind more than one surface. src/routes/screener.ts is now a thin handler that validates with a Zod schema and calls it:
return await runScreener({ network, filters, sortBy, order, cursor, limit })Your branch extracts the same logic as screenPairs inside src/routes/screener.ts. So we have two extractions of one query, and git cannot reconcile them — merging main gives 266 conflicted lines across three regions in that file alone.
I stopped there rather than picking a winner by hand. This is the query behind published volume and ranking numbers, and a merge I resolve by choosing between two restructurings is exactly where a subtle wrong answer hides. You know what you intended; I would be guessing.
What the rebase needs
- Drop the
screenPairsextraction.src/routes/screener.tsonmainis already thin — leave it alone. - Point
lensTools.tsat the service:import { runScreener } from '../services/screener'. Note it issrc/services/, notsrc/routes/. - Check the argument shape. You currently go through
stringifyScreenerArgsbecause the route parsed strings.runScreenertakes typed values —{ network, filters: { volume, change_24h_min, ... }, sortBy, order, cursor, limit }— so the MCP tool can pass numbers straight through and that helper can go. The route still stringifies only because query parameters arrive as strings; a tool's arguments do not. - Keep
listPairsandlistPools. Neither exists onmain—/poolsis still inline inrest.tsandpairs.tsexports nolistPairs— so those extractions are yours and conflict with nothing conceptually. src/mcp/server.tsandsrc/api/rest.tshave one conflict region each, both small.
Two things worth knowing that landed while you were out, because they affect the tool surface:
- Limit clamping lives in
runScreener, not the route:Math.min(SCREENER_MAX_LIMIT, Math.max(1, limit ?? SCREENER_DEFAULT_LIMIT)). Passlimitthrough and the tool is clamped identically to REST and GraphQL for free. Do not re-clamp in the tool — one clamp or they drift. sortByandorderare allow-listed inside the service before being interpolated into the SQL, since a column name cannot be a bind parameter. That protection is inrunScreener, so going through it is also what keeps the tool from becoming an injection surface. Worth a test assertinglens_screen_pairswithsortBy: "price; DROP TABLE"is refused rather than passed along.
After the rebase: npx tsc --noEmit, npm test, and if you touch openapi.yaml at all, npm run openapi:gen plus npx --yes openapi-typescript@7.13.0 openapi.yaml -o clients/typescript/src/schema.d.ts — both are byte-compared in CI now.
Separately: #423 is closed and credited to you. You were right that the work was already on main — it went in with #487 (94bd8ea), which is why #490's branch was empty after the rebase. I checked it rather than taking it on trust, and the sigValidOnMainnet: false assertion in that test is the best thing in it; details on the issue.
Closes #409
Summary
MCP exposed prices (L213) but nothing for discovery. An agent couldn't list
which pairs exist, see the liquidity behind them, or find what's moving, so it
had to guess asset codes. This PR adds three MCP tools that wrap the existing
/pairs,/poolsand/screenerlogic.Changes
lens_list_pairs,lens_get_poolsandlens_screen_pairsMCP tools./pairs,/poolsand/screenerintoshared service functions. The REST routes and MCP tools both call them, so
there is a single implementation and no duplicated queries.
with a
next_cursor/offsetfield for fetching the next page.lens_list_pairs: enumerate which pairs exist, e.g. to discover validasset codes before asking for a price.
lens_get_pools: inspect the liquidity behind a specific pair.lens_screen_pairs: find pairs by movement or criteria, e.g. top movers.Pagination
Pass the
next_cursorfrom a previous response ascursorto get the nextpage. When
next_cursorisnull, there are no more results. The parametersare documented in each tool's input schema and in
docs/mcp.md.Testing
assert the payloads match.
cursor yields the next page with no overlap.
GABC…XYZ).Checklist