Skip to content

fix(tools): report the ListMcpResources cap in resources, not bytes - #425

Open
kevin9327 wants to merge 1 commit into
leookun:mainfrom
kevin9327:fix/mcp-resource-truncation-notice
Open

fix(tools): report the ListMcpResources cap in resources, not bytes#425
kevin9327 wants to merge 1 commit into
leookun:mainfrom
kevin9327:fix/mcp-resource-truncation-notice

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

Follow-up in the area 0f564c0 ("fix(tools): preserve accurate truncation notices") just curated.

The bug

gate_mcp_resources caps the list by resource count:

success.resources.truncate(MCP_RESOURCE_LIMIT);   // 200 resources

…and then describes that truncation with truncation_notice, which is written for byte budgets, handed MCP_TEXT_LIMIT:

description: Some(truncation_notice(
    "ListMcpResources",
    MCP_TEXT_LIMIT,          // 32768 -- a text budget this path never applies
    success.resources.len(),
    original,
)),
fn truncation_notice(tool_name: &str, limit: usize, shown: usize, original: usize) -> String {
    format!("[truncated: {tool_name} result exceeded {limit} bytes; showing {shown} of {original} bytes]")
}

An MCP server returning 250 resources therefore produces:

[truncated: ListMcpResources result exceeded 32768 bytes; showing 200 of 250 bytes]

Neither figure describes what happened. 32768 is a limit this path never applies, and the counts are resources, not bytes.

This string is not diagnostic-only: it is the description of the truncated:list-mcp-resources sentinel resource appended to the list, so it is what the model reads to learn why the list is short. As written it invites the conclusion that the list was cut for size and would fit under a smaller byte budget, when the cap is a flat count and nothing about resource size would change it.

The fix

State the cap that was actually applied, in its own unit — matching the wording the sibling item-count truncation in gate_mcp already uses 130 lines above:

"[truncated: MCP content items exceeded {MCP_CONTENT_ITEM_LIMIT} items; showing {MCP_CONTENT_ITEM_LIMIT} of {original_items} items]"

truncation_notice is left alone; every one of its other callers really is gating bytes.

Verification

cargo +1.95 test --package cursor-server --lib tool_call_result::gate

Before (notice reverted, test kept) — the assertion prints the exact shipped string:

---- list_mcp_resources_reports_the_cap_it_actually_applied stdout ----
assertion `left == right` failed
  left: Some("[truncated: ListMcpResources result exceeded 32768 bytes; showing 200 of 250 bytes]")
 right: Some("[truncated: ListMcpResources result exceeded 200 resources; showing 200 of 250 resources]")

test result: FAILED. 6 passed; 1 failed

After:

test cursor::tools::tool_call_result::gate::tests::list_mcp_resources_reports_the_cap_it_actually_applied ... ok
test cursor::tools::tool_call_result::gate::tests::mcp_total_text_truncation_always_adds_a_notice ... ok
test cursor::tools::tool_call_result::gate::tests::truncate_text_never_exceeds_its_limit ... ok
test cursor::tools::tool_call_result::gate::tests::truncate_text_terminates_when_the_notice_length_oscillates ... ok
test cursor::tools::tool_call_result::gate::tests::truncate_text_reports_the_actual_utf8_prefix_size ... ok
test cursor::tools::tool_call_result::gate::tests::grep_content_gate_survives_a_nearly_exhausted_byte_budget ... ok
test cursor::tools::tool_call_result::gate::tests::grep_content_gate_terminates_on_an_oscillating_remaining_budget ... ok

test result: ok. 7 passed; 0 failed

gate_mcp_resources had no test before this; the new one also pins that the sentinel is appended and carries the truncated:list-mcp-resources uri, which is the marker the function uses to stay idempotent.

Gates (make check, Rust half):

  • cargo fmt --all -- --check — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean on CI's stable; see note
  • cargo test --workspace --all-targets — all suites green

Toolchain note: this machine's stable is broken, so the gates ran with +1.95. Clippy 1.95 reports a collapsible_match error at server/src/cursor/compile/model.rs:109 on unmodified main; that is a 1.95-only false positive (its own suggestion, "fast" if parse_bool(parameter)? =>, does not compile — ? is not allowed in a match guard) and CI's 1.98.1 does not emit it, so main is green. Clippy here therefore ran with -A clippy::collapsible_match; this diff is unaffected either way.

`gate_mcp_resources` caps the list at `MCP_RESOURCE_LIMIT` (200 resources),
then describes that truncation with `truncation_notice`, which is written
for byte budgets and was handed `MCP_TEXT_LIMIT`. A server returning 250
resources produced:

    [truncated: ListMcpResources result exceeded 32768 bytes; showing 200 of 250 bytes]

Neither figure describes what happened: 32768 is a text budget this path
never applies, and the counts are resources rather than bytes. The notice
goes into a sentinel resource's description, so it is what the model reads
to learn why the list is short -- and it invites the conclusion that the
list was cut for size and would fit under a smaller byte budget.

State the cap that was actually applied, in its own unit, matching the
wording the sibling item-count truncation in `gate_mcp` already uses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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