MCP budget: shrink get_query_heatmap's default cell cap and text preview (#4198) - #4259
Merged
Merged
Conversation
Part of #4198. A default call on a busy server measured 144,757 bytes - 4.4x the shared 32 KB budget - because 500 cells alone, before any query text, already ran past it. Cuts the default cell cap to 100 and the per-cell query text preview to 80 characters, adds a per-cell top_query_text_truncated flag and a full_text opt-in for the whole statement, on both Darling and Lite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
Part of #4198. top_query_text_truncated is a read-side preview cut (the full text is already in the store; full_text opts back into it), not a page cut or a source-side one, so it needed its own roster class rather than joining an existing one that would have misdescribed it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
erikdarlingdata
marked this pull request as ready for review
September 25, 2026 07:44
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
Both heatmap (#4198, +147 bytes) and audit_config (#4192/#4195/#4193/#4217, +82 bytes) independently bumped TotalCeilingBytes from the same base. Merged value: 171_637 + 147 + 82 = 171_866. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
All deltas: audit_config +82, deadlock_detail +364, plan_corrections +137, heatmap +147 (Darling); deadlock_detail +253, plan_corrections +137, heatmap +147 (Lite). Census uses FieldPreviewCutKeys (dev's rename). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
ReadSidePreviewCutKeys (branch's own class, not in Concat chain) held top_query_text_truncated for get_query_heatmap. Merged into FieldPreviewCutKeys and removed the unused ReadSidePreviewCutKeys declaration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
This was referenced Sep 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #4198.
Why
get_query_heatmap(Darling and Lite) measured 144,757 bytes / 1,763 ms at default arguments on a busyproduction server. It was the largest #4198 offender after the Query Store regressions tool. The shared
budget (
McpResponseBudget.DefaultBytes) is 32 KB.A heatmap payload is CELLS (time bins times magnitude buckets) plus TEXT. I measured which one dominates
first. At 500 cells (the old default), the five fixed per-cell fields alone run to roughly 75 KB before a
single byte of query text. That is more than double the budget. Shrinking the text preview alone did not
fix this. The cell cap came down regardless of text width.
What changes
Both products, identically:
magnitude buckets. That is most of a business day on a server whose queries land in two or three
buckets. Raising
bucket_minutesstill covers more of the window in the same cell count.top_query_textpreview: 120 to 80 characters. This alone barely moves the total, since the fixedper-cell structure dominates, as measured above.
top_query_text_truncatedfield on every cell: true whenever the stored statement is longer than thepreview shown, at either preview length.
full_textargument (default false): returns each cell's top query at full length instead of the80-character preview. The full length is bounded at 32,000 characters. No real T-SQL statement reaches
that. An explicit ask still gets what it asks for, per the MCP read tools have no default response-size budget: at default arguments 12 tools return >50 KB and 3 return >100 KB for one server, more than an agent client's per-result cap #4198 ruling, even past the budget.
full_textfield echoed at the top level so a caller who never inspects a cell still learns a secondcall gets more.
LEFT(query_text, 120)becomes a bound parameter on both SKUs (Postgresdate_bin/DuckDBtime_bucketqueries). It fetches preview-length-plus-one so the extra character is the truncationsignal. This is the same over-fetch-by-one idiom the cell cap already used. No second round trip, no
computed
LENGTH()column.McpPayloadContractCensusTests(Darling):top_query_text_truncatedis a genuinely new kind of cut thiscensus had no class for. The full text is already in the store. A caller opts back in with
full_textrather than re-paging. That is the opposite direction from a source-side cut where nothing is left to
fetch. Added a new
ReadSidePreviewCutKeysroster. The existing classes do not fit this shape.McpToolsListBudgetpins (both SKUs): added thefull_textparameter line (94 bytes) and raised eachproduct's
TotalCeilingBytesby the measured +147-byte growth.ExemptOffendersroster, soMcpReadToolBudgetLiveTestswas left untouched, as directed.separate, unapproved issue. Out of scope here, not touched.
Measured bytes
New live/DuckDB tests seed 60 fully-populated 5-minute bins: 420 cells, well past both the old and new cap.
Each cell has a realistic 227-character two-table-join statement and an 18-character hash
(
CONVERT(varchar(64), …, 1)'s real width). This reproduces the reported shape rather than a minimal one.< 32,768, same assertionsfull_text=true>defaultDefault stays under the 32,768-byte budget with about 5,150 bytes (15.7%) of headroom.
I proved the regression test fails on the pre-fix code. I reverted the two source files, rebuilt, and
confirmed the new test does not compile against the old 7-parameter signature. Reapplied, and it passes.
Test plan
Darling.Testsfull suite once, on a fresh UTC rig (port 55976): 13,844 total, 4 failed at first pass.One failure (
McpPayloadContractCensusTests) was mine: the new field needed classifying, fixed andre-verified in isolation (68/68 pass). The other three -
CaptureDownChunkOrderTests,ServerListAndSummaryPlanShapeTests,TrendPayloadBudgetLiveTests- fail in isolation on this rig. Theytouch no file this PR changes (TimescaleDB chunk-pruning plan shape and a trend tool's stream read).
Dev's latest completed Build run (36103277984) was green. They look environmental: a dozen MCP read tools have no default response-size budget: at default arguments 12 tools return >50 KB and 3 return >100 KB for one server, more than an agent client's per-result cap #4198 lanes
ran a full Postgres rig with 74 background workers on this machine tonight. I did not re-run the full
suite a third time after the census fix. The deadline did not allow it. Flagging for the coordinator to
recheck on a quieter machine.
Lite.Testsfull suite once: 5,338 total, 0 failed.DarlingQueryHeatmapBudgetLiveTestsandQueryHeatmapBudgetTests(Lite) both green, plus theexisting
DarlingQueryHeatmapTests(36/36) andMcpToolsListBudgetTests(both SKUs) green.McpReadToolBudgetLiveTests(not on the A budget pin over every MCP read tool (part of #4198) #4224 roster for this tool).Installer.Testsnot run (out of scope per lane rules).CHANGELOG entry
SECTION: Fixed
ENTRY:
get_query_heatmapdefault call stays under the MCP response budget ([MCP budget: shrink get_query_heatmap's default cell cap and text preview (#4198) #4259]): A default call returnedover 144 KB on a busy server. That is more than 4x the 32 KB budget. The default cell cap drops from 500 to 100.
The top-query-text preview per cell drops from 120 to 80 characters. Each cell now reports
top_query_text_truncated. Passfull_text=trueto get full query text per cell. The Darling web viewer'sheatmap panel shows 80-character query previews instead of 120.
REF:
[MCP budget: shrink get_query_heatmap's default cell cap and text preview (#4198) #4259]: MCP budget: shrink get_query_heatmap's default cell cap and text preview (#4198) #4259
Generated with Claude Code
https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ