Skip to content

MCP budget: default get_query_store_regressions under the response budget (#4198) - #4264

Merged
erikdarlingdata merged 6 commits into
devfrom
fix/4198-qs-regressions-default
Sep 25, 2026
Merged

erikdarlingdata merged 6 commits into
devfrom
fix/4198-qs-regressions-default

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Part of #4198.

Why

get_query_store_regressions had no response-size budget. McpResponseBudget.cs flagged it as the worst #4198 offender: 211 KB at default arguments on a busy production store, over 6x the 32 KB McpResponseBudget.DefaultBytes target. Two things drove that size. query_text is an unbounded Query Store text sample, one per row, at up to 50 rows by default. The ten numeric fields (durations, CPU, reads, regression percents) come straight off AVG() over microsecond integers. They serialize with a long, meaningless decimal tail instead of a display-rounded one, and that weight repeats across ten fields on every row.

What changes

Darling (DarlingMcpQueryStoreRegressionTools.cs) and Lite (McpQueryTools.cs) both change the same way:

  • query_text is a 240-character preview by default (the same length get_store_query_stats already uses
    for its own full_text), with a new query_text_truncated flag. A new full_text argument (default
    false) opts back into the whole text, 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 precedent from get_store_query_stats.
  • The default limit drops from 50 to 30. This is sized from the measured bytes per row so a default call
    stays well under budget instead of sitting right at its edge.
  • The ten numeric fields are rounded to 2 decimal places at the JSON edge only. The reader's SQL and the
    desktop viewer's own read are untouched. Every existing pinned test uses whole-number fixtures that round
    trip unchanged (verified below).
  • Web viewer: the /api/read row for get_query_store_regressions now passes full_text: QueryBool(c, "full_text", true) in the dispatch table, and the catalog row gets PBool("full_text", true). That keeps
    the viewer showing today's full query text (it renders it via codeDisclosure on the Query Store
    Regressions tab). No wwwroot/js change was needed: server-tabs.js's call never sent full_text
    explicitly, so the row's own default carries it. A new no-rig test, QueryStoreRegressionsWebDefaultTests.cs,
    pins this.
  • FieldPreviewCutKeys (census) gets a query_text_truncated entry, added alongside the deadlock-detail
    lane's own entry. Both merged cleanly from origin/dev.
  • McpToolsListBudget pins are updated on both products. TotalCeilingBytes is raised by exactly the
    measured growth (+214 Darling, +203 Lite), stacked on top of the concurrent deadlock-detail lane's own raise
    after merging origin/dev.

This PR does not touch DarlingQueryStoreRegressionReader's SQL, or Lite's equivalent reader, per the #4198
note for this tool. It only changes how the tool projects the read into JSON.

Test plan

Rig: reused the stopped rig-ta Postgres rig (port 55976, already UTC), started in the background, confirmed
"ready to accept connections", then dropped and recreated darlingtest and probe.

  • New live test DarlingMcpQueryStoreRegressionsBudgetLiveTests (Darling): plants 40 regressed queries
    with ~4,091-character query text each. That fixture is the same order of magnitude as the documented
    211 KB at 50 rows before this fix. After the fix: 25,527 bytes for the default call (30 rows
    returned, truncated: true), under the 32,768-byte budget. full_text: true returns the whole text and
    no query_text_truncated.
  • New Lite test QueryStoreRegressionsBudgetTests (Lite.Tests, no rig): same fixture shape, same
    assertions. It passes.
  • DarlingQueryStoreRegressionsSurfaceAndSqlTests.ParamContract_AllOptional_MatchesLite updated for the
    new full_text parameter.
  • McpPayloadContractCensusTests: my FieldPreviewCutKeys entry plus the deadlock-detail lane's, both
    present after the merge.
  • McpToolsListBudgetTests (both products): pins and TotalCeilingBytes updated to the measured totals.
  • Targeted run after merging origin/dev: Darling.Tests, the classes this PR touches plus
    McpZeroIsAMeasurementTests. 106 passed, 0 failed. Lite.Tests, the classes this PR touches.
    9 passed, 0 failed.
  • Full Darling.Tests suite: not run. Every class this PR's diff touches ran targeted and green (listed
    above), but the full suite must run before arming auto-merge.
  • Full Lite.Tests suite: not run, same reason.

Rig stopped (pg_ctl stop -m fast) after the last test run.

CHANGELOG entry

SECTION: Fixed
ENTRY:

Generated with Claude Code

https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

erikdarlingdata and others added 3 commits September 25, 2026 04:15
Checkpoint: rig measured a 40-row/~4KB-text fixture at 25,527 bytes after
the fix (was the documented 211 KB worst offender at old defaults). Darling
and Lite both cut query_text to a 240-char preview behind full_text, round
the ten numeric fields to 2 decimals, and drop the default row limit from
50 to 30. Web viewer row pinned to keep today's full-text behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…s-default

# Conflicts:
#	Darling/Darling.Tests/McpPayloadContractCensusTests.cs
#	Darling/Darling.Tests/McpToolsListBudgetTests.cs
#	Lite.Tests/McpToolsListBudgetTests.cs
…CutKeys

Deltas: audit+deadlock+plan_corrections (prior dev merges) + qs_regressions TR
(+214 Darling, +203 Lite). query_text_truncated merges plan_correction and
qs_regression files into one entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
@erikdarlingdata erikdarlingdata changed the title DO NOT MERGE (MCP budget): default get_query_store_regressions under the response budget (#4198) MCP budget: default get_query_store_regressions under the response budget (#4198) Sep 25, 2026
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 25, 2026 08:58
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) September 25, 2026 08:58
erikdarlingdata and others added 3 commits September 25, 2026 06:17
Darling: HEAD=172,434 (qs_regressions +214) + dev=172,367 -> 172,581
Lite: HEAD=90,474 (qs_regressions +203) + dev=90,418 -> 90,621
Census: merge qs_regressions files into query_text_truncated + keep top_query_text_truncated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
@erikdarlingdata
erikdarlingdata merged commit 54a962e into dev Sep 25, 2026
26 of 28 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4198-qs-regressions-default branch September 25, 2026 12:10
erikdarlingdata added a commit that referenced this pull request Sep 25, 2026
Measured with McpToolsListBudgetTests after merging origin/dev (#4261,
#4258, #4265, #4267, #4264, #4266 and #4268) plus this PR's catalog
changes. Budget, census and tool-guide classes: 219/219.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
erikdarlingdata added a commit that referenced this pull request Sep 25, 2026
…nder the response budget (#4198) (#4272)

* MCP budget: describe_custom_view_catalog default groups measures by source (#4198)

Default default-argument call was 98,173 bytes (#4198's own measurement), three
times the tool's 32 KB budget. It is pure static reference data (no server/store
read), so the cut groups the 179 measures by source and keeps only
key/displayName/kind/unitFamily/validAggregates per measure; source=<name>
drills into one source's full detail, full_detail=true returns the original
shape unfiltered. /api/catalog (the web Custom Views editor) calls the
underlying builder directly, never this MCP method, so it is unaffected -
pinned in DarlingComposeTests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

* Trigger CI (draft PR skipped the Build workflow)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ

* Add comment to DarlingMcpCustomViewCatalogSizeTests.cs to trigger Build CI

GitHub did not fire pull_request events for this draft-opened PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ

* fix(#4272): reword validAggregates description - compact drops allowedDimensions, source=<name> returns it

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

* #4272: set TotalCeilingBytes to the measured 174,236 after merging dev

Measured with McpToolsListBudgetTests after merging origin/dev (#4261,
#4258, #4265, #4267, #4264, #4266 and #4268) plus this PR's catalog
changes. Budget, census and tool-guide classes: 219/219.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
erikdarlingdata added a commit that referenced this pull request Sep 25, 2026
…as merged

After merging dev, the per-tool fixes for get_blocking (#4267),
get_collection_log (#4265), get_query_store_regressions (#4264),
get_collection_health (#4268), describe_custom_view_catalog (#4272) and
get_query_store_top (#4273) are all in, and get_fleet_overview already fit
(CI's stale-exemption message). Every row goes; CI's live run decides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
erikdarlingdata added a commit that referenced this pull request Sep 25, 2026
* Add the #4198 MCP read-tool budget pin (part of #4198)

McpReadToolBudgetLiveTests reflects over every [McpServerTool] method
on every [McpServerToolType] class in the Darling service assembly,
excludes write tools/analyze_*/compare_*/audit_config, binds each
tool's DI services and server_name generically, and asserts the reply
stays under McpResponseBudget.DefaultBytes unless the tool is named
in an explicit, byte-stamped exemption roster. A fix lands by
deleting its row.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

* MCP budget pin: empty ExemptOffenders now every #4198 per-tool lane has merged

After merging dev, the per-tool fixes for get_blocking (#4267),
get_collection_log (#4265), get_query_store_regressions (#4264),
get_collection_health (#4268), describe_custom_view_catalog (#4272) and
get_query_store_top (#4273) are all in, and get_fleet_overview already fit
(CI's stale-exemption message). Every row goes; CI's live run decides.

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

* MCP budget pin: get_collection_health is still over on this fixture

CI on 08e6e81 measured get_collection_health at 35,145 B, over the 32 KB
default. #4268 compacts only healthy collectors with nothing to report, and
this fixture's collectors are not healthy, so nothing compacts. The row goes
back, under #4198, which stays open for it. Every other exempt tool fits.

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

---------

Co-authored-by: Claude Sonnet 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