Skip to content

fix(mcp): cap get_chains, report totalMatched, add status filter#100

Merged
Johnaverse merged 2 commits into
mainfrom
feat/get-chains-cap-and-status
Jul 15, 2026
Merged

fix(mcp): cap get_chains, report totalMatched, add status filter#100
Johnaverse merged 2 commits into
mainfrom
feat/get-chains-cap-and-status

Conversation

@Johnaverse

Copy link
Copy Markdown
Owner

Problem

Asked to "list the chains", the assistant said the total was 1257 — which is totalTestnets, not the ~2932 total — then couldn't list them. Root cause: get_chains returned all ~2932 chains as full objects (with a price lookup for each). That payload overflows the model's context, so it lost the real total and reused a category count from an earlier get_stats.

Fix (mcp-tools.js)

  • Cap the returned list (default 50, max 200) and return totalMatched (full count matching the filter), count (returned), and truncated. The honest total is now always in the response even when the list is capped.
  • Only price-enrich the returned slice — not all 2932 chains.
  • Add a status filter (active / incubating / deprecated / unknown) so "list deprecated/active chains" works directly.
  • Clearer description: totalMatched is the full count, the list is a sample, never treat a tag/category count as the overall total; use get_stats for exact totals and search_chains to find a specific chain.

Verification

Against the live registry:

args totalMatched count truncated
{} 2932 50 true
{status:'deprecated', limit:3} 141 3 true
{status:'active'} 2693 50 true
{tag:'L2', status:'deprecated'} 33 33 false

Unit tests added (cap, default cap, totalMatched, truncated, status filter); full suite 919 passed / 4 skipped; lint clean.

Deploy

Server-side MCP/assistant change — ships on the next version bump.

🤖 Generated with Claude Code

The assistant answered "list chains" with 1257 (the testnet count) as the
total and couldn't list them. Root cause: get_chains returned all ~2932
chains as full objects (with a price lookup each) — a payload that overflows
the model's context, so it lost the true total and reused a category count
from an earlier get_stats.

- Cap the returned list (default 50, max 200) and return totalMatched (full
  count matching the filter), count (returned), and truncated. The honest
  total is now always in the response.
- Only price-enrich the returned slice, not all 2932 chains.
- Add a `status` filter (active/incubating/deprecated/unknown) so "list
  deprecated/active chains" works directly.
- Description tells the model totalMatched is the full count, the list is a
  sample, and never to treat a tag/category count as the overall total;
  points at get_stats for exact totals and search_chains to find one.

Verified against the live registry: {} -> totalMatched 2932 count 50;
status=deprecated -> 141; status=active -> 2693; tag=L2+deprecated -> 33.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvKASF2guQdnLuivCfTPH8
Copilot AI review requested due to automatic review settings July 15, 2026 00:24

Copilot AI 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.

Pull request overview

This PR updates the MCP get_chains tool to avoid returning the full registry payload (and doing price enrichment for every chain), instead returning a capped sample plus metadata (totalMatched, count, truncated) and adding a lifecycle status filter so assistants can directly list active/deprecated/incubating chains without losing the true totals.

Changes:

  • Cap get_chains results (default 50, max 200) while reporting totalMatched and truncated, and only price-enrich the returned slice.
  • Add status filtering (active / incubating / deprecated / unknown) to get_chains.
  • Add unit tests covering capping/defaults, totalMatched/truncated, and status filtering.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
mcp-tools.js Implements the capped get_chains response shape, adds status and limit inputs, and limits price enrichment to the returned slice.
tests/unit/mcp-tools.test.js Adds unit tests validating the new get_chains capping/metadata behavior and lifecycle status filtering.

Comment thread mcp-tools.js
// the true total. Cap the list, report totalMatched so callers still get the
// honest count, and only price-enrich the slice actually returned.
const totalMatched = chains.length;
const limit = Math.max(1, Math.min(Number(args.limit) || GET_CHAINS_DEFAULT_LIMIT, GET_CHAINS_MAX_LIMIT));
Comment thread mcp-tools.js
Comment on lines +53 to +56
limit: {
type: 'number',
description: 'Max chains to return (default 50, max 200)',
},
Per feedback: the 50-item cap is fine, but the assistant must say it's
showing a subset (and never state a wrong total). Extend the get_chains
description so that when `truncated` is true the model tells the user it's
showing the first `count` of `totalMatched` (a display limit) and offers to
narrow by tag/status or search — instead of presenting the sample as the
whole list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvKASF2guQdnLuivCfTPH8
Copilot AI review requested due to automatic review settings July 15, 2026 00:37

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment thread mcp-tools.js
// the true total. Cap the list, report totalMatched so callers still get the
// honest count, and only price-enrich the slice actually returned.
const totalMatched = chains.length;
const limit = Math.max(1, Math.min(Number(args.limit) || GET_CHAINS_DEFAULT_LIMIT, GET_CHAINS_MAX_LIMIT));
Comment thread mcp-tools.js
Comment on lines +53 to +56
limit: {
type: 'number',
description: 'Max chains to return (default 50, max 200)',
},
Comment on lines +306 to +315
it('applies the default cap (50) when no limit is given', async () => {
const many = Array.from({ length: 130 }, (_, i) => ({ chainId: i + 1, name: `Chain ${i + 1}`, tags: [] }));
vi.mocked(dataService.getAllChains).mockReturnValue(many);

const result = await handleToolCall('get_chains', {});
const data = JSON.parse(result.content[0].text);
expect(data.count).toBe(50);
expect(data.totalMatched).toBe(130);
});

@Johnaverse
Johnaverse merged commit f3e4ccb into main Jul 15, 2026
6 checks passed
@Johnaverse
Johnaverse deleted the feat/get-chains-cap-and-status branch July 15, 2026 01:24
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.

2 participants