Skip to content

MCP budget: get_blocking / get_blocked_process_reports default under 32 KB (#4198) - #4267

Merged
erikdarlingdata merged 7 commits into
devfrom
fix/4198-blocking-default
Sep 25, 2026
Merged

erikdarlingdata merged 7 commits into
devfrom
fix/4198-blocking-default

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Part of #4198.

Why

get_blocking (Darling) / get_blocked_process_reports (Lite) had no response-size budget. Each row
carries about 37 fields. That includes isolation levels, client app/host/login for both sides, six
last-tran/last-batch stamps, and a dedup_key, before either SQL text column is counted. So the default 30-row page was already heavy.

A live test seeded 30 rows with realistic-but-modest SQL text: sub-2000 characters, so never truncated under the old cap. It measured 89,096 bytes for blocked/blocking text combined, 2.7x the shared 32 KB McpResponseBudget.DefaultBytes. The wide row shape, not the two text columns alone, was most of that weight.

What changes

  • Darling (DarlingMcpBlockingTools.cs, get_blocking): default row limit halves, 30 -> 15
    (DefaultLimit). blocked_sql_text / blocking_sql_text preview to 150 chars
    (SqlTextPreviewLength) instead of the old fixed 2000, each with its own *_truncated flag. A new
    full_text opt-in returns both columns whole. A dedup_key call, which already names one incident,
    always gets the whole text too, the same exemption get_deadlock_detail uses for full_graph.
  • The public MCP method is now a thin wrapper. It calls a new internal overload that takes an explicit
    sqlTextPreviewLength, the same split Trend tools return every raw point: get_file_io_trend sends 12,451 points / 1.4 MB for one server at defaults, more than an LLM client's context #3897's trend tools use for TrendBudget.Chart.
  • Web viewer: the /api/read row for get_blocking now calls that internal overload directly. It
    passes the OLD row limit (30, already hardcoded there) and the OLD 2000-char preview
    (WebSqlTextPreviewLength). The web page's result is unchanged. I traced both call sites through
    panels.js's readTool(), which hits GET /api/read/get_blocking: wwwroot/js/pages/server-tabs.js's
    Blocking table and wwwroot/js/view-templates.js's Custom Views template. Both already send
    limit: 30 explicitly and neither sends full_text, so both keep today's result with no JS changes.
  • Lite (McpBlockingTools.cs, get_blocked_process_reports): same default-limit halving, same
    150-char preview, same full_text opt-in. Lite has no dedup_key on this tool, so it needs no
    exemption for it. Lite has no web viewer to pin.
  • Census: added blocked_sql_text_truncated / blocking_sql_text_truncated to
    McpPayloadContractCensusTests.FieldPreviewCutKeys on both SKUs.
  • tools/list budget: DarlingMcpBlockingTools.txt and Lite's McpBlockingTools.txt get the new
    full_text param line and the tool's grown byte count. TotalCeilingBytes is raised by the exact
    measured growth on each SKU (+391 Darling, +271 Lite), with a change-log line above each constant.
  • Lite parity test (this lane): Lite.Tests/McpPageContractTests.cs gained
    GetBlockedProcessReports_Default_StaysUnderResponseBudget_WithThirtyWideReports, next to lane TB's
    GetDeadlockDetail_Default_StaysUnderResponseBudget_WithFiveWideGraphs precedent. Plants 30 reports
    with both text fields near 700-870 characters and every other field populated. Asserts the default call
    previews under McpResponseBudget.DefaultBytes, and that full_text still returns the whole text on
    every row.
  • Head-length fix (this lane): the full Darling suite (step 4) found a defect in lane TI's own
    change. It had pushed get_blocking's served head (the part before <<GUIDE>>) from 620 to 781
    characters, over McpToolGuideTests' D3 cap. Adding the new preview sentence there was the cause.
    Moved that sentence into the tail instead, the same way get_plan_corrections' full_text note lives
    in its tail and not its head. Every HeadFacts substring McpToolGuideHeadsBlockingSlotsTests requires
    for get_blocking stays verbatim in the head. The head now measures 576 served characters. Banked
    DarlingMcpBlockingTools.txt's per-tool ceiling from 781 down to 576 to hold the saving.

#4224 note

Your tool (get_blocking) is on #4224's ExemptOffenders roster. Per this lane's brief, #4224 has not
merged and its wait was lifted by the coordinator. McpReadToolBudgetLiveTests is not on dev, so this
PR does not create or edit it. Whichever of #4224 or this PR merges second must remove this tool's row
from that roster. After this PR, get_blocking is no longer an offender.

Test plan

  • Live test Darling/Darling.Tests/DarlingMcpBlockingBudgetLiveTests.cs (own file, own seed, not the
    shared DarlingMcpBlockingToolsLivePostgresTests seeding). It plants 30 rows with realistic 701/872-char
    blocked/blocking SQL text.
    • Before this fix: 89,096 bytes (measured, then reverted to confirm before writing the fix).
    • After this fix: 26,745 bytes, under the 32,768-byte budget. full_text: true and a dedup_key call
      both return the whole text. The default call previews with *_truncated: true.
  • Lite.Tests/McpPageContractTests.cs's new GetBlockedProcessReports_Default_... test: plants 30
    reports with ~700-870-char text on both fields. Measured 23,969 bytes at the default call, under the
    32,768-byte budget. full_text: true returns all 30 rows' text whole and untruncated. Proved once that
    the test fails against the old shape. Temporarily reverted SqlTextPreviewLength to 2000 and limit's
    default to 30, rebuilt, and confirmed it then returns 30 rows instead of 15. Reverted back and confirmed
    green. 24/24 passed in McpPageContractTests.
  • McpPayloadContractCensusTests (Darling.Tests): 68/68 passed after adding the two new keys and
    merging in origin/dev's own new top_query_text_truncated key alongside them.
  • McpToolsListBudgetTests (Darling.Tests and Lite.Tests): both 5/5 passed. Resolved the merge
    conflict on TotalCeilingBytes by keeping every change-log comment from both sides. Set each constant
    to the measured total: Darling 172,758 bytes, Lite 90,689 bytes.
  • git merge origin/dev: three conflicts (McpPayloadContractCensusTests.cs,
    McpToolsListBudgetTests.cs on both SKUs), resolved as above.
  • Full Darling.Tests suite, once, against a fresh darlingtest on a UTC rig: 13,887 total, 2 failed,
    47 skipped, 1 not run. Both failures are pre-existing, not this branch's:
    • EventWindowedReadsAreBoundedLivePostgresTests.TheFlooredReads_PlanAtMostThreeChunksForA24HourWindow_AgainstDevPostgres
      fails the same way alone on a freshly created database. Dev's own latest CI Build run (commit
      f5e001fe, the exact commit this branch merged) fails it too: Darling PG tests (0), run
      36115696476. It is unrelated TimescaleDB chunk-floor planning for job_history, not blocking.
    • CaptureDownChunkOrderTests.TheShippedRead_ExecutesOnlyTheNewestChunk_AndTheNewestRunDecides_AgainstDevPostgres
      passed cleanly when re-run alone on a fresh database. It only failed inside the full 13,887-test run.
      That points at shared-state or timing inside that one big run, not a defect. Neither file is one this
      PR touches.
  • Full Lite.Tests suite, once: 5,346 total, 0 failed.

Deferred

Nothing filed as a separate issue. The two full-suite failures above are pre-existing on dev. They are
outside this PR's subsystem: TimescaleDB chunk-floor planning for job_history/collection_log, not
blocking. I verified them against dev's own latest CI rather than filing anything, since diagnosing that
subsystem is past this lane's brief. Worth a census entry at the next wave boundary.

CHANGELOG entry

SECTION: Fixed
ENTRY:

Generated with Claude Code

https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

erikdarlingdata and others added 5 commits September 25, 2026 04:42
…onse budget

30 default rows at ~37 fields/row already ran 89,096 bytes with sub-2000-char
(never-truncated) SQL text -- 2.7x the 32 KB budget -- before either text
column was even the driver. Halves the default row limit (30 -> 15) and
previews blocked_sql_text/blocking_sql_text to 150 chars (full_text opts back
in; a dedup_key call always gets the whole text). Web viewer keeps today's
30-row, 2000-char-preview result through an internal GetBlocking overload the
/api/read row calls explicitly, the way #3897's trend tools pass
TrendBudget.Chart. Lite twin gets the same defaults.

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

FieldPreviewCutKeys gets blocked_sql_text_truncated / blocking_sql_text_truncated
(both SKUs). McpToolsListBudget pins and each TotalCeilingBytes ceiling move
with the new full_text parameter, on both Darling and Lite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
Lite had the same default-limit-halving and SQL-text-preview fix as
Darling's get_blocking, but no budget test pinning it. Plants 30 reports
with blocked/blocking text near 700-870 characters and asserts the
default call stays under McpResponseBudget.DefaultBytes while full_text
still returns the whole text. Proved once (temporarily reverting
SqlTextPreviewLength to 2000 and limit to 30) that it fails on the old
defaults, then reverted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
Resolved McpPayloadContractCensusTests.FieldPreviewCutKeys (kept both
lane TI's blocked/blocking text keys and dev's top_query_text key) and
both McpToolsListBudgetTests.TotalCeilingBytes conflicts by keeping
every change-log comment and setting the constant to the measured
total: Darling 172,758 bytes, Lite 90,689 bytes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
The full-suite run this lane ran as step 4 caught what lane TI's own
change had done: adding the blocked_sql_text/blocking_sql_text preview
note to get_blocking's served head pushed it from 620 to 781 characters,
over McpToolGuideTests' D3 ceiling. Moved the note to the tail (after
<<GUIDE>>), which is not served in tools/list, the same way
get_plan_corrections' full_text note lives in its tail rather than its
head. Every HeadFacts substring McpToolGuideHeadsBlockingSlotsTests
requires stays verbatim in the head, which now measures 576 served
characters. Banked DarlingMcpBlockingTools.txt's per-tool ceiling from
781 to 576 to hold the saving.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
@erikdarlingdata erikdarlingdata changed the title DO NOT MERGE (MCP budget): get_blocking / get_blocked_process_reports default under 32 KB (#4198) MCP budget: get_blocking / get_blocked_process_reports default under 32 KB (#4198) Sep 25, 2026
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 25, 2026 09:53
@erikdarlingdata
erikdarlingdata merged commit 4cd9cd3 into dev Sep 25, 2026
15 of 16 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4198-blocking-default branch September 25, 2026 11:47
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