Skip to content

fix(catalog): restore shape-discipline + Date-conversion exampleCalls - #32

Merged
Aliiiu merged 7 commits into
mainfrom
fix/restore-shape-and-date-fixes
Jun 23, 2026
Merged

fix(catalog): restore shape-discipline + Date-conversion exampleCalls#32
Aliiiu merged 7 commits into
mainfrom
fix/restore-shape-and-date-fixes

Conversation

@Aliiiu

@Aliiiu Aliiiu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Context

On 2026-06-23 at 12:57 UTC, main was force-pushed from a compromised maintainer credential, rewinding 10 commits and inserting an obfuscated payload into vitest.config.ts plus an axios downgrade in package.json.

PR #31 (Version Packages, merged at 15:48 UTC) restored a clean tip — the malicious payload is gone, axios is 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)

  • Narrow-vs-broad guidance in descriptions of every broad list endpoint (getProtocols, getChains, getDexsOverview, getFeesOverview, getOptionsOverview, getLatestPools, getStablecoins, getStablecoinChains).
  • Response shaping in every exampleCallsort+slice+project for lists, slice-tail+project for time-series, pluck-specific-fields for single-entity summaries, nested coins[key] unwrap for price.* endpoints.
  • New always-loaded instructions.md section "Shape responses inside execute() — don't ship raw payloads back".
  • Upstream-shape correctness in getProtocol exampleCall — /protocol/{slug} returns tvl as a 2012-entry array, not a number.
  • getHistoricalPoolData unwrap — response shape is { data: [] }, not a bare array.
  • Null/undefined guards on the pool-lookup-then-fetch chain.

From PR #30 (time-series-date-conversion)

  • * 1000 Unix-seconds-to-ms conversion on the three /v2-style time-series endpoints (getHistoricalChainTvl, getStablecoinCharts, getStablecoinPrices).
  • getHistoricalPoolData left alone (its timestamp is already an ISO string); asymmetry noted via inline comment.

What is 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 changes to test files, services, or build tooling.

Test plan

  • pnpm exec tsc --noEmit clean
  • pnpm test → 171 passed
  • Regenerated embedded-index.ts + instructions.generated.ts; CI's gen-files-diff gate confirms the build output matches.
  • Manual diff vs the lost good state (7fbee13): byte-identical for the restored content.
  • Post-merge: confirm a fresh Aiden session no longer hits Q1 context overflow, Q11 response-unwrap failures, or Q3/Q7 5-10min post-processing on time-series.

Why this isn't a force-push

The window for safely restoring via --force-with-lease closed when PR #31 was merged at 15:48 (origin tip moved away from 9e05f6f). A counter-force-push at this point would invalidate any local copies of the new main that the team has already pulled. Append-only restoration is the safer path.

🤖 Generated with Claude Code

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>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/mcp/catalog/tool-metadata.ts Outdated
Comment thread src/mcp/search-docs/embedded-index.ts Outdated
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>
@Aliiiu

Aliiiu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/mcp/catalog/tool-metadata.ts Outdated
Comment thread src/mcp/catalog/tool-metadata.ts Outdated
Comment thread src/mcp/catalog/tool-metadata.ts Outdated
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>
@Aliiiu

Aliiiu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/mcp/instructions/instructions.md Outdated
Comment thread src/mcp/instructions/instructions.generated.ts Outdated
…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>
@Aliiiu

Aliiiu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/mcp/catalog/tool-metadata.ts Outdated
Comment thread src/mcp/search-docs/embedded-index.ts Outdated
Comment thread src/mcp/catalog/tool-metadata.ts Outdated
Comment thread src/mcp/catalog/tool-metadata.ts Outdated
Comment thread src/mcp/search-docs/embedded-index.ts Outdated
Comment thread src/mcp/search-docs/embedded-index.ts Outdated
…-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>
@Aliiiu

Aliiiu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/mcp/catalog/tool-metadata.ts Outdated
…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>
@Aliiiu

Aliiiu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/mcp/catalog/tool-metadata.ts
Comment thread src/mcp/catalog/tool-metadata.ts Outdated
…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>
@Aliiiu

Aliiiu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@Aliiiu
Aliiiu merged commit e7725d9 into main Jun 23, 2026
1 check passed
@github-actions github-actions Bot mentioned this pull request Jun 23, 2026
@Aliiiu Aliiiu self-assigned this Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant