MCP budget: get_blocking / get_blocked_process_reports default under 32 KB (#4198) - #4267
Merged
Merged
Conversation
…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
marked this pull request as ready for review
September 25, 2026 09:53
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
3 tasks done
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>
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_blocking(Darling) /get_blocked_process_reports(Lite) had no response-size budget. Each rowcarries 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
DarlingMcpBlockingTools.cs,get_blocking): default rowlimithalves, 30 -> 15(
DefaultLimit).blocked_sql_text/blocking_sql_textpreview to 150 chars(
SqlTextPreviewLength) instead of the old fixed 2000, each with its own*_truncatedflag. A newfull_textopt-in returns both columns whole. Adedup_keycall, which already names one incident,always gets the whole text too, the same exemption
get_deadlock_detailuses forfull_graph.internaloverload that takes an explicitsqlTextPreviewLength, 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 forTrendBudget.Chart./api/readrow forget_blockingnow calls that internal overload directly. Itpasses 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 throughpanels.js'sreadTool(), which hitsGET /api/read/get_blocking:wwwroot/js/pages/server-tabs.js'sBlockingtable andwwwroot/js/view-templates.js's Custom Views template. Both already sendlimit: 30explicitly and neither sendsfull_text, so both keep today's result with no JS changes.McpBlockingTools.cs,get_blocked_process_reports): same default-limit halving, same150-char preview, same
full_textopt-in. Lite has nodedup_keyon this tool, so it needs noexemption for it. Lite has no web viewer to pin.
blocked_sql_text_truncated/blocking_sql_text_truncatedtoMcpPayloadContractCensusTests.FieldPreviewCutKeyson both SKUs.DarlingMcpBlockingTools.txtand Lite'sMcpBlockingTools.txtget the newfull_textparam line and the tool's grown byte count.TotalCeilingBytesis raised by the exactmeasured growth on each SKU (+391 Darling, +271 Lite), with a change-log line above each constant.
Lite.Tests/McpPageContractTests.csgainedGetBlockedProcessReports_Default_StaysUnderResponseBudget_WithThirtyWideReports, next to lane TB'sGetDeadlockDetail_Default_StaysUnderResponseBudget_WithFiveWideGraphsprecedent. Plants 30 reportswith both text fields near 700-870 characters and every other field populated. Asserts the default call
previews under
McpResponseBudget.DefaultBytes, and thatfull_textstill returns the whole text onevery row.
change. It had pushed
get_blocking's served head (the part before<<GUIDE>>) from 620 to 781characters, 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_textnote livesin its tail and not its head. Every
HeadFactssubstringMcpToolGuideHeadsBlockingSlotsTestsrequiresfor
get_blockingstays verbatim in the head. The head now measures 576 served characters. BankedDarlingMcpBlockingTools.txt's per-tool ceiling from 781 down to 576 to hold the saving.#4224 note
Your tool (
get_blocking) is on #4224'sExemptOffendersroster. Per this lane's brief, #4224 has notmerged and its wait was lifted by the coordinator.
McpReadToolBudgetLiveTestsis not ondev, so thisPR 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_blockingis no longer an offender.Test plan
Darling/Darling.Tests/DarlingMcpBlockingBudgetLiveTests.cs(own file, own seed, not theshared
DarlingMcpBlockingToolsLivePostgresTestsseeding). It plants 30 rows with realistic 701/872-charblocked/blocking SQL text.
full_text: trueand adedup_keycallboth return the whole text. The default call previews with
*_truncated: true.Lite.Tests/McpPageContractTests.cs's newGetBlockedProcessReports_Default_...test: plants 30reports with ~700-870-char text on both fields. Measured 23,969 bytes at the default call, under the
32,768-byte budget.
full_text: truereturns all 30 rows' text whole and untruncated. Proved once thatthe test fails against the old shape. Temporarily reverted
SqlTextPreviewLengthto 2000 andlimit'sdefault 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 andmerging in
origin/dev's own newtop_query_text_truncatedkey alongside them.McpToolsListBudgetTests(Darling.Tests and Lite.Tests): both 5/5 passed. Resolved the mergeconflict on
TotalCeilingBytesby keeping every change-log comment from both sides. Set each constantto the measured total: Darling 172,758 bytes, Lite 90,689 bytes.
git merge origin/dev: three conflicts (McpPayloadContractCensusTests.cs,McpToolsListBudgetTests.cson both SKUs), resolved as above.Darling.Testssuite, once, against a freshdarlingteston 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_AgainstDevPostgresfails 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), run36115696476. It is unrelated TimescaleDB chunk-floor planning for
job_history, not blocking.CaptureDownChunkOrderTests.TheShippedRead_ExecutesOnlyTheNewestChunk_AndTheNewestRunDecides_AgainstDevPostgrespassed 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.
Lite.Testssuite, 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 areoutside this PR's subsystem: TimescaleDB chunk-floor planning for
job_history/collection_log, notblocking. 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:
get_blocking(Darling) andget_blocked_process_reports(Lite) cap their response size ([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]): A default call measured 89,096 bytes on a 30-row test seed, over the 32 KB limit. The default row limit drops from 30 to 15. The blocked and blocking SQL text come back as a 150-character preview instead of a 2,000-character cut, and each has its own*_truncatedflag. A newfull_textoption returns both texts whole. On Darling, a call withdedup_keynames one incident, so it always gets the whole text. The web viewer's Blocking table and Custom Views keep 30 rows and the 2,000-character text.REF:
[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]: 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
Generated with Claude Code
https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3