fix(catalog): restore shape-discipline + Date-conversion exampleCalls - #32
Conversation
Re-applies the substantive content from the merged-then-lost PRs #28 (shape-large-payloads) and #30 (time-series-date-conversion), on top of current main (post-PR-#31). Same code/diff as those original PRs, just authored against the recovered base instead of restoring history via force-push. Background: main was force-pushed at 2026-06-23 12:57 UTC from a compromised maintainer credential, rewinding 10 commits and inserting an obfuscated payload into vitest.config.ts plus an axios downgrade in package.json. The payload was removed by a forward-fix in PR #31, but PR #31 only restored a clean tip — it did not bring back the substantive work from PRs #28 and #30. This PR closes that gap without another force-push. What's restored: * Narrow-vs-broad guidance in descriptions of every broad list endpoint (getProtocols, getChains, getDexsOverview, getFeesOverview, getOptionsOverview, getLatestPools, getStablecoins, getStablecoinChains). * Response shaping in every exampleCall — sort+slice+project for lists, slice-tail+project for time-series, pluck-specific-fields for single-entity summaries, nested coins[key] unwrap for price.* endpoints. * The * 1000 Unix-seconds -> ms conversion on the three /v2-style time-series endpoints (getHistoricalChainTvl, getStablecoinCharts, getStablecoinPrices). The fourth (getHistoricalPoolData) is left alone because its timestamp is already an ISO string; asymmetry noted inline. * Upstream-shape correctness in getProtocol's exampleCall — the /protocol/{slug} endpoint returns tvl as a 2012-entry array, not a number. The example now plucks the latest entry from that array. * getHistoricalPoolData unwrap — response shape is { data: [] }, not a bare array. exampleCall uses (series.data ?? []).slice(). * Null guards on the pool-lookup-then-fetch chain so an empty find() result doesn't TypeError or trip Zod validation. * New always-loaded instructions.md section "Shape responses inside execute() — don't ship raw payloads back". What's deliberately not changed: * vitest.config.ts stays clean (the malicious payload is not re-introduced). * axios stays at ^1.12.2 (no re-downgrade). * No test, service, or build-tool changes. Locally verified: - pnpm exec tsc --noEmit clean - pnpm test -> 171 passed - regenerated embedded-index.ts + instructions.generated.ts Original PRs whose content this restores: #28, #30. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request restores response-shaping discipline, Unix-to-milliseconds date conversions, and narrow-vs-broad endpoint guidance across tool metadata, instructions, and search documentation to prevent large raw payloads from overflowing the agent's context. The review feedback correctly identifies a potential runtime TypeError in the getProtocol example call where p.tvl?.[p.tvl.length - 1] is evaluated; if p.tvl is undefined, accessing its length will crash. The reviewer suggests using p.tvl?.at(-1) as a safer and cleaner alternative in both tool-metadata.ts and embedded-index.ts.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Switches `p.tvl?.[p.tvl.length - 1]` to the cleaner `p.tvl?.at(-1)`
in the getProtocol exampleCall. Two improvements:
- Doesn't reference `p.tvl` twice (one optional chain instead of
two reads).
- Uses the canonical ES2022 idiom for "last element".
Note on the reviewer's stated reason — the bot claimed the
original `p.tvl?.[p.tvl.length - 1]` would TypeError when
`p.tvl` is nullish. That's incorrect: optional chaining
short-circuits BEFORE evaluating the index expression (verified
empirically with `node -e` against null / undefined / empty
array / populated array cases — all four return undefined or
the expected entry, no throw). But the cleaner form is still
worth taking on code-quality grounds.
171 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request restores previously lost work on response shaping and date conversion for the DefiLlama MCP server. It updates tool metadata descriptions and exampleCall fields to enforce size discipline, promote narrow endpoints, and handle Unix-to-JS date conversions correctly. Additionally, a new instructions section has been added to guide response shaping inside the execute() sandbox. The review feedback highlights several potential runtime errors in the new exampleCall snippets where resolver functions (such as resolveProtocol, resolveChain, and resolveStablecoin) could return null, suggesting the addition of robust null guards and consistent error objects to prevent validation or type failures.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
defillama.resolveChain / resolveProtocol / resolveStablecoin all
return null when the input doesn't match the upstream catalog.
The exampleCalls were either destructuring the result (which
TypeErrors on null) or passing it straight through to a Zod-typed
endpoint (which fails validation). Either way the agent ends up
with a confusing error envelope instead of a graceful short-circuit.
Apply the early-return guard uniformly across all 16 resolver-using
exampleCalls so the pattern is consistent end-to-end and matches
the one already established for the pool-UUID lookup in
getHistoricalPoolData:
Protocol slug: const slug = await defillama.resolveProtocol(name);
if (!slug) return { error: 'Protocol not found' };
...
Chain (replaces destructure form):
const chain = await defillama.resolveChain(input);
if (!chain) return { error: 'Chain not found' };
... chain.name / chain.slug ...
Stablecoin id: const id = await defillama.resolveStablecoin(symbol);
if (!id) return { error: 'Stablecoin not found' };
...
Sites updated (16 exampleCalls):
- getProtocol, getDexSummary, getFeesSummary, getOptionsSummary
(resolveProtocol guards)
- getHistoricalChainTvl, getDexsOverview, getFeesOverview,
getOptionsOverview (resolveChain — destructure replaced)
- getStablecoinCharts (resolveChain + resolveStablecoin, two guards)
- getCurrentPrices, getFirstPrices, getBatchHistorical,
getHistoricalPrices, getPercentageChange, getPriceChart
(all six price.* endpoints — resolveChain destructure replaced)
- getBlockAtTimestamp (resolveChain destructure replaced)
Bot also called out a `return` was missing before the
`getBlockAtTimestamp` call (it wasn't returning a value previously
— example dropped the result). Added the explicit `return` while in
the area so the example actually surfaces the block height to the
agent.
Counts verified: 12 resolveChain guards, 4 resolveProtocol guards,
1 resolveStablecoin guard. Zero destructure-from-null patterns
remaining in exampleCalls.
Caught by gemini-code-assist[bot] on PR #32 (3 of the 16 sites
explicitly; the rest applied for consistency). Same null-guard
discipline as the pool-UUID guard from PR #28.
171 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request restores shape-discipline and time-series Date-conversion fixes that were previously lost on main. It updates tool metadata, embedded indexes, and instruction files to guide the model to shape API responses inside the execute() sandbox instead of returning raw payloads. The feedback highlights an inconsistency in the newly added instructions (instructions.md and instructions.generated.ts), where the time-series example does not demonstrate the Unix-seconds-to-milliseconds date conversion (* 1000) that was restored elsewhere in this PR.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…s example The "Time-series" pattern in the always-loaded "Shape responses" section of instructions.md was using bare `p.date` without the * 1000 conversion — inconsistent with the per-endpoint exampleCalls in tool-metadata.ts (which DO convert), and would produce 1970-01-XX dates if the agent followed the instruction verbatim. This is a latent bug from the original PR #28: the instruction code block was written before PR #30 introduced the Date-conversion discipline, and PR #30 only updated the per-endpoint examples — not this canonical instruction. The restoration PR brought the instruction back verbatim along with the inconsistency. Fix: the time-series pattern now demonstrates `new Date(p.date * 1000).toISOString()` and has a one-line note explicitly calling out the Unix-seconds-vs-ms gotcha so the rule is teachable from the instructions surface, not only from the per-endpoint examples. Regenerated instructions.generated.ts to match. Caught by gemini-code-assist[bot] cross-referencing the instruction example against the tool-metadata exampleCalls — same cross-check pattern that caught the changeset-vs-code mismatch earlier in PR #28. 171 tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request restores shape-discipline and time-series Date-conversion improvements across tool metadata, search documentation, and instructions to prevent large raw payloads from overflowing the agent's context. The review feedback identifies a syntax error in the arrow functions of some example calls (which lack a body or expression) and warns against in-place array mutations (.sort()) on potentially cached data, recommending copying the arrays first to avoid side effects.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…-list examples Two distinct concerns surfaced by gemini-code-assist[bot] on PR #32. 1. SYNTAX ERROR (real, critical). The getHistoricalPoolData exampleCall had an arrow function with no body: (pools.data ?? []).find(p => /* match on project/symbol/chain */) Verified empirically: SyntaxError: Unexpected token ')' An agent following this example verbatim would crash at parse time. Replaced the bare comment with a real matching expression using the *Input naming convention used elsewhere: (pools.data ?? []).find(p => p.project === projectInput && p.symbol === symbolInput && p.chain === chainInput )?.pool 2. IMMUTABLE-SORT PATTERN (style fix, wrong rationale, right code). The bot claimed `chains.sort()` would mutate the cached host-side response. That's incorrect for this codebase's architecture: execute/client.ts wraps every service response in `ivm.ExternalCopy(...).copyInto(...)`, so the sandbox gets a structured clone — mutating it inside the sandbox cannot reach the host-side cache. Verified by inspecting envelopeOk in src/mcp/execute/client.ts. But the suggested `[...arr].sort(...)` form is still a better teaching pattern: - Signals immutability intent. - Safe to copy-paste into non-sandbox contexts (a Node script, a different MCP host, etc.) where `.sort()` WOULD mutate. - Same well-known JS idiom. Accepted on style/teaching grounds. Applied uniformly to all six broad-list sort sites (the two the bot flagged plus four it didn't, for consistency): - getChains, getProtocols, getDexsOverview, getFeesOverview, getOptionsOverview, getLatestPools. Also updated the matching "Lists" example in instructions.md's "Shape responses inside execute()" section so the canonical instruction demonstrates the same pattern, with a one-line note explaining when/why the spread matters. 171 tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request restores response-shaping discipline and Unix-to-JS date conversion fixes across various DefiLlama MCP tool metadata definitions, generated instructions, and documentation indexes. Specifically, it updates tool descriptions and exampleCall properties to demonstrate sorting, slicing, plucking specific fields, and handling Unix-seconds-to-milliseconds conversions to prevent context overflows and date parsing issues. The review feedback suggests converting the asOf field in the getProtocol example call to an ISO string using new Date(last.date * 1000).toISOString() for consistency with other date-handling examples.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…example The `getProtocol` exampleCall returned `asOf: last?.date` as a raw Unix-seconds number — inconsistent with the other time-series examples in the catalog (getHistoricalChainTvl, getStablecoinCharts, getStablecoinPrices) which all convert via `new Date(p.date * 1000) .toISOString()`. Same class of inconsistency as the latent bug fixed earlier in this PR for instructions.md's time-series example. Fix: `asOf: last?.date ? new Date(last.date * 1000).toISOString() : undefined` The conditional preserves `undefined` when there is no last entry, which matches the existing pattern where `last?.totalLiquidityUSD` and other plucked fields can also be `undefined` for an empty tvl array. Caught by gemini-code-assist[bot] on PR #32 via the same catalog-wide cross-check pattern that caught the instructions.md and changeset-vs-code mismatches earlier. 171 tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request restores response-shaping, size-discipline, and date-conversion improvements across various DefiLlama MCP endpoints that were previously lost. It updates tool metadata, generated instructions, and embedded search indexes to include robust example calls demonstrating how to filter, sort, slice, and project fields inside the sandbox instead of returning raw payloads. The review feedback suggests refining the example calls for stablecoin endpoints to correctly flatten nested objects (circulating and totalCirculatingUSD) into primitive values to ensure clean payloads.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…culatingUSD
Two related upstream-shape inconsistencies in the stablecoin
exampleCalls — both confirmed against StablecoinChainsSchema's
declaration AND a live probe of stablecoins.llama.fi:
GET /stablecoins?includePrices=true
peggedAssets[].circulating = { peggedUSD: number }
// NOT a primitive
GET /stablecoinchains
[].totalCirculatingUSD = { peggedUSD: number }
// NOT a primitive
Before this fix, the exampleCalls projected the wrapper object
through verbatim — `circulating: s.circulating` returned
`{peggedUSD: 186079265199.30408}`, and `total: r.totalCirculatingUSD`
returned `{peggedUSD: 6926947.82...}`. That defeats the "flat
projected shape" goal of the surrounding shape-discipline work, and
gives the agent a confusingly-shaped object where it would expect a
number.
Apply `?.peggedUSD` unwrap to both. Inline comment notes the upstream
wrapper convention so the next person editing these examples doesn't
re-introduce the bug.
Caught by gemini-code-assist[bot] on commit 672a4aa via the same
schema-vs-example cross-check pattern that found the getProtocol
tvl-array and getHistoricalPoolData {data} wrapper bugs earlier on
this PR.
171 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request restores response-shaping discipline, Unix-to-millisecond Date conversions, and robust null/undefined guards across various DefiLlama MCP tool definitions, generated instructions, and search documentation. These changes ensure that large payloads are trimmed, projected, and filtered inside the execution sandbox before being returned, preventing context overflow and performance issues. I have no additional feedback to provide as the changes are well-implemented and correctly documented.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Context
On 2026-06-23 at 12:57 UTC,
mainwas force-pushed from a compromised maintainer credential, rewinding 10 commits and inserting an obfuscated payload intovitest.config.tsplus anaxiosdowngrade inpackage.json.PR #31 (Version Packages, merged at 15:48 UTC) restored a clean tip — the malicious payload is gone,
axiosis back at^1.12.2— but it did NOT bring back the substantive work from the merged-then-lost PRs #28 and #30.This PR closes that gap without another force-push. Same code/diff as those original PRs, just authored against the recovered base.
Scope (everything restored)
From PR #28 (shape-large-payloads)
getProtocols,getChains,getDexsOverview,getFeesOverview,getOptionsOverview,getLatestPools,getStablecoins,getStablecoinChains).exampleCall—sort+slice+projectfor lists,slice-tail+projectfor time-series, pluck-specific-fields for single-entity summaries, nestedcoins[key]unwrap forprice.*endpoints.instructions.mdsection "Shape responses insideexecute()— don't ship raw payloads back".getProtocolexampleCall —/protocol/{slug}returnstvlas a 2012-entry array, not a number.getHistoricalPoolDataunwrap — response shape is{ data: [] }, not a bare array.From PR #30 (time-series-date-conversion)
* 1000Unix-seconds-to-ms conversion on the three/v2-style time-series endpoints (getHistoricalChainTvl,getStablecoinCharts,getStablecoinPrices).getHistoricalPoolDataleft alone (itstimestampis already an ISO string); asymmetry noted via inline comment.What is deliberately NOT changed
vitest.config.tsstays clean — the malicious payload is not re-introduced.axiosstays at^1.12.2— no re-downgrade.Test plan
pnpm exec tsc --noEmitcleanpnpm test→ 171 passedembedded-index.ts+instructions.generated.ts; CI's gen-files-diff gate confirms the build output matches.7fbee13): byte-identical for the restored content.Why this isn't a force-push
The window for safely restoring via
--force-with-leaseclosed when PR #31 was merged at 15:48 (origin tip moved away from9e05f6f). A counter-force-push at this point would invalidate any local copies of the newmainthat the team has already pulled. Append-only restoration is the safer path.🤖 Generated with Claude Code