Skip to content

feat(mcp-server): output shaping & truncation framework (MCP Prompt 16)#4017

Open
gilgardosh wants to merge 2 commits into
claude/mcp-prompt-15-report-toolfrom
claude/mcp-prompt-16-output-truncation
Open

feat(mcp-server): output shaping & truncation framework (MCP Prompt 16)#4017
gilgardosh wants to merge 2 commits into
claude/mcp-prompt-15-report-toolfrom
claude/mcp-prompt-16-output-truncation

Conversation

@gilgardosh

Copy link
Copy Markdown
Collaborator

Summary

Implements Prompt 16 – Output shaping and truncation framework (see
docs/mcp/spec.md §9.3). Adds a centralized
formatter that all list-producing tools use.

Stacked PR: targets claude/mcp-prompt-15-report-tool (Prompt 15, #4016).
Review/merge Prompts 09→15 first; this diff shows only the Prompt 16 changes.

Changes

  • src/tools/output.tsshapeListResult:
    • Enforces MAX_TOOL_RESULT_BYTES by dropping whole trailing items (binary search over prefix length) so output is always valid JSON — never a structure cut mid-object.
    • Reports returnedCount / totalCount / truncated, and attaches a continuation hint (payload_size vs result_cap) whenever not all results were returned.
  • Retrofitcharges, list_tags, list_tax_categories, and balance_report now build results through the shared formatter; the per-tool ad-hoc truncated/total fields are unified into returnedCount/totalCount/truncated/continuation.
  • Tests — within-limit, result-cap continuation, payload-size guard (whole-item drops, valid JSON ≤ cap, prefix preserved, zero-fit), plus updated tool tests for the new shape. 173 tests total.

Validation

  • yarn workspace @accounter/mcp-server test → 173 passed
  • yarn workspace @accounter/mcp-server lint / typecheck / build → pass

Notes

  • Unified truncation semantics: truncated = returnedCount < totalCount, so both an upstream row cap and the byte guard surface the same way with a continuation hint. Tools pass the true upstream total (e.g. pageInfo.totalRecords, filtered count) so pagination "more available" is reflected.
  • Continuation is a hint, not a cursor — the tools are page/param-driven, so the hint points the caller to narrow filters or request the next/smaller page. A real opaque cursor could replace it later if desired.
  • The default byte cap (60 KB) is conservative; it can be tuned once we observe real client limits.

🤖 Generated with Claude Code


Generated by Claude Code

@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 16:08 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 16:08 — with GitHub Actions Inactive

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

Copy link
Copy Markdown
Contributor

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 introduces a centralized output shaping and truncation utility (shapeListResult) to enforce payload size limits on list-producing tools, updating the charges, lookups, and reports tools to use it. The review feedback suggests spreading params.extra first in the structured output to prevent accidental key overrides, and improving the lookup tools' summaries to explicitly show the number of returned items when truncated.

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 packages/mcp-server/src/tools/output.ts
Comment thread packages/mcp-server/src/tools/lookups.ts Outdated
Comment thread packages/mcp-server/src/tools/lookups.ts Outdated
@gilgardosh
gilgardosh force-pushed the claude/mcp-prompt-15-report-tool branch from 7dc10a7 to 2ef1248 Compare July 21, 2026 16:19
Add a centralized formatter enforcing bounded, valid tool output (spec §9.3).

- src/tools/output.ts: shapeListResult caps the serialized payload
  (MAX_TOOL_RESULT_BYTES) by dropping whole trailing items via binary search
  (never invalid JSON), reports returnedCount/totalCount/truncated, and adds a
  continuation hint (payload_size vs result_cap) when not all results are shown
- retrofit all three tools (charges, tags/tax-categories, balance report) to
  build their results through the shared formatter; per-tool ad-hoc truncated
  flags removed in favor of the unified fields
- unit tests for within-limit, result-cap continuation, payload-size guard
  (whole-item drops, valid JSON, prefix preserved, zero-fit), plus updated tool
  tests for the new output shape

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPMszz9gdZFhNjobRm6Ndo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a centralized output shaping/truncation helper for MCP list-style tools, enforcing a byte cap by dropping whole trailing items (preserving valid JSON) and standardizing returnedCount/totalCount/truncated plus a continuation hint.

Changes:

  • Introduced shapeListResult (src/tools/output.ts) implementing byte-based truncation via whole-item drops and consistent metadata fields.
  • Retrofitted list-producing tools (charges, lookups (tags/tax categories), balance_report) to use the shared formatter and updated their structured output shape.
  • Added/updated tests to validate within-limit behavior, result-cap truncation, payload-size truncation, and updated tool output assertions.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/mcp-server/src/tools/reports.ts Uses shapeListResult for balance report rows and unified truncation metadata.
packages/mcp-server/src/tools/output.ts New shared output shaping/truncation framework with byte cap + continuation hint.
packages/mcp-server/src/tools/lookups.ts Refactors tag/tax-category tools to return shaped list results with unified counts/truncation.
packages/mcp-server/src/tools/charges.ts Routes charge search output through the shared shaper; moves total count to totalCount.
packages/mcp-server/src/tools/tests/reports.test.ts Updates balance report expectations to the new structured shape (totalCount, etc.).
packages/mcp-server/src/tools/tests/output.test.ts Adds unit tests for shapeListResult (result cap + payload-size guard).
packages/mcp-server/src/tools/tests/lookups.test.ts Updates lookups tests to assert totalCount and continuation reason semantics.
packages/mcp-server/src/tools/tests/charges.test.ts Updates charges test assertions for totalCount/truncated and pagination shape.
packages/mcp-server/README.md Documents the new shared formatter and unified truncation semantics for list tools.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/mcp-server/src/tools/output.ts
Address review feedback on output.ts:
- Normalize `total` to never be less than `items.length`, so a caller passing
  a too-small `total` can't produce `totalCount < returnedCount` or a wrong
  `truncated` flag.
- Spread `extra` before the framework-owned keys so a colliding `extra` key can
  never overwrite the items array or the counts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPMszz9gdZFhNjobRm6Ndo
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 22, 2026 11:59 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 22, 2026 11:59 — with GitHub Actions Inactive
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.

3 participants