Skip to content

rust(fix): write list metadata ahead of the items in the text result - #847

Merged
alexa-sift merged 8 commits into
mainfrom
rust/unmatched-fields-before-items
Oct 30, 2026
Merged

alexa-sift merged 8 commits into
mainfrom
rust/unmatched-fields-before-items

Conversation

@alexa-sift

@alexa-sift alexa-sift commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Background: Every list_* MCP tool builds its response through list_body. A caller can pass fields: ["name", "asset_id"] to keep only those keys on each row. If a requested name matches nothing on any row (a typo like nmae), list_body adds unmatched_fields: ["nmae"]. Without that key, a typo would quietly return rows with that field missing, and the agent could report the data as empty when the field name was wrong. unmatched_fields lets the agent catch the mistake and retry, or at least tell the user.

The problem: serde_json sorts keys alphabetically, so unmatched_fields comes after the items array. In 7 of the 14 list tools (channels, assets, annotations, and others), the items key also sorts before count and has_more. The agent output guard cuts large responses from the end, so these keys disappear on exactly the big responses where they matter most.

The fix: list_body now returns a ListBody that carries the items key, and its into_result method writes the text block agents read with every metadata key first and the items array last. All 14 list tools return through it, so callers name the items key once and can't let it drift. Tools that add keys after the fact (artifact_url, next_step) do so through ListBody::insert. count, has_more, unmatched_fields, artifact_url, and next_step now survive head truncation regardless of how the items key sorts or whether serde_json's preserve_order is ever enabled. structuredContent is unchanged, and the key keeps its existing name. sift_mcp/CLAUDE.md now teaches the same pattern.

Verification

  • New into_result tests in common/test.rs:
    • All 14 items keys write count, has_more, and unmatched_fields before the items.
    • With 500 rows, all three sit in the first 200 characters.
    • The text block parses to the same JSON as structuredContent.
    • next_step gets its own leading block.
  • Handler-level assertions on the text block for list_channels, list_artifact_versions (artifact_url first), and list_calculated_channel_versions (next_step block first).
  • With the text block reverted to alphabetical output, 6 of the new tests fail.
  • cargo fmt --check --all, cargo check --all-features, and cargo clippy --all-features pass with no new warnings. cargo test -p sift_mcp passes 605 tests.

Rename the list tools' unmatched_fields key to _unmatched_fields so it
sorts ahead of the items and survives head truncation of large
responses. Document the key in each list tool's Output section and add
tests for the ordering and the descriptions.
@alexa-sift
alexa-sift marked this pull request as ready for review October 26, 2026 19:28
alexa-sift and others added 4 commits October 26, 2026 12:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the `_unmatched_fields` rename with a `list_result` helper. The text
block agents read now writes `count`, `has_more`, `unmatched_fields` and other
metadata before the items, so they survive head truncation in every list tool,
including the 7 whose items key sorts before `count`. `structuredContent` is
unchanged, and the key keeps its original `unmatched_fields` name.

ENG-15730

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alexa-sift
alexa-sift marked this pull request as draft October 29, 2026 18:47
The bullets were added so agents would learn the `_unmatched_fields` rename.
With the rename reverted, each tool's `fields` bullet already names the key,
so remove the bullets, their docs guard test, and the CHANGELOG mention.

ENG-15730

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alexa-sift
alexa-sift marked this pull request as ready for review October 30, 2026 16:55
Comment thread rust/crates/sift_mcp/CLAUDE.md Resolved
alexa-sift and others added 2 commits October 30, 2026 16:15
`list_body` now returns a `ListBody` that remembers its items key, and
`ListBody::into_result` replaces `list_result`, so callers no longer pass the
key twice and cannot let the two drift into alphabetical output. Tools that add
keys after the fact (`artifact_url`, `next_step`) go through
`ListBody::insert`.

Point the sift_mcp CLAUDE.md handler template, the step 4 return rule and the
read tools bullet at `list_body(...).into_result(...)` for list tools.

ENG-15730

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alexa-sift alexa-sift changed the title rust(fix): keep unmatched fields visible when list output is truncated rust(fix): write list metadata ahead of the items in the text result Oct 30, 2026
@alexa-sift
alexa-sift requested a review from bill-sift October 30, 2026 23:30
@alexa-sift
alexa-sift merged commit a514165 into main Oct 30, 2026
22 checks passed
@alexa-sift
alexa-sift deleted the rust/unmatched-fields-before-items branch October 30, 2026 23:42
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