Repository navigation
MCP budget: size get_object_locking's default rows (#4198) - #4258
Merged
Merged
Conversation
…et (#4198) get_object_locking had a hardcoded 200-row cap with no override and no truncation signal: 71,332 bytes at default arguments on a busy production store, more than double McpResponseBudget.DefaultBytes. Both SKUs now take an optional limit (default 75, over-fetched by one and bound through McpHelpers.BoundPage the way get_index_usage already does), with truncated/objects_returned/note added to the envelope so a short answer is never mistaken for a complete one. An explicit limit still gets what it asks for, up to McpHelpers.MaxTop. 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:19
erikdarlingdata
enabled auto-merge (squash)
September 25, 2026 07:20
erikdarlingdata
disabled auto-merge
September 25, 2026 07:30
# Conflicts: # Darling/Darling.Tests/McpToolsListBudgetTests.cs # Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpObjectStatsTools.cs
… dev (#4258) get_object_locking's new limit MCP default (75) replaces the old hardcoded 200-row fetch that had no override. The web viewer's /api/read row calls the same MCP method, so without its own default it would silently drop from 200 rows to 75. Pin the row's limit to 200, the same value the neighboring get_index_usage row already pins for the identical reason, and add a source-text pin so a future edit that drops the argument fails a test instead of silently shrinking the page the viewer renders. Also resolves the origin/dev merge conflict this branch had: dev had already lowered get_index_usage's own default (IndexUsageTop) from 200 to 75 in a separate PR, while this branch lowered ObjectLockingTop the same way. Kept both constants at their own new value, and re-measured McpToolsListBudgetTests.TotalCeilingBytes with both changes applied (171,797 bytes) rather than adding the two PRs' deltas by hand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
All three deltas now in: audit_config +82, deadlock_detail +364, object_locking +79 (Darling); audit_config +0, deadlock_detail +253, object_locking +79 (Lite). No new conflicts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
Incorporates plan_corrections (+137 Darling/Lite) from dev (#4257). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
erikdarlingdata
enabled auto-merge (squash)
September 25, 2026 09:30
Darling: HEAD=172,298 (object_locking +79) + dev=172,367 -> 172,446 Lite: HEAD=90,350 (object_locking +79) + dev=90,418 -> 90,497 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
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>
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_object_locking(Darling and Lite) had a hardcoded 200-row cap. It had no override and no truncationsignal. Field measurement on a busy production store: 71,332 bytes at default arguments, 460 ms. That is more
than double
McpResponseBudget.DefaultBytes(32 KB). The issue's ruling applies: size each tool's defaultsfrom its own measured bytes per row, and keep the envelope honest with
truncated/*_returned. An explicitargument still gets what it asks for.
This tool has no long free-text field to preview or truncate, unlike query text or deadlock XML. Every field is
a short name or a number. So the fix is entirely a row-count default, the same shape
get_index_usagealreadyuses in this file.
What changes
get_object_lockingbodies (Darling'sDarlingMcpObjectStatsTools.cs, Lite'sMcpObjectStatsTools.cs)take a new optional
limitparameter (int, default 75, validated withMcpHelpers.ValidateTop).limit + 1. The result is bound withMcpHelpers.BoundPage, the sameover-fetch-by-one pattern several other Darling tools already use.
truncatedis OBSERVED, not inferred.objects_returned,truncated, and anotefield, mirroringget_index_usage's wordingstyle. Rows are still ordered by total wait time descending, so a truncated page is the highest-contention
indexes, not an arbitrary cut.
GetIndexLockingAsync/LocalDataService.GetIndexLockingAsyncalreadytook a row-count parameter. Only the value passed to it, and the parameter/envelope around it, changed. I
deliberately left
DarlingObjectStatsReader.DatabaseSizeLatestSqlandget_index_usageuntouched: both areother lanes' territory tonight per my brief.
Darling.Tests/DarlingMcpObjectStatsToolsTests.cs:get_object_lockingmoved off theParamContract_ServerNameOnly_Optionaltheory, since it is no longer server-name-only, into its ownObjectLocking_TakesALimit_Optionalfact, mirroring the existingget_index_usageone. This was the onefailure the full suite caught (a param-contract pin, not a defect), and it is fixed here.
McpToolsListBudgetTests.cs(both products): the newlimitparameter added 79 bytes totools/list. Theper-tool budget file gained
param get_object_locking.limit 35, and eachTotalCeilingBytesmoved byexactly that measured amount, with a change-log line.
Darling/Darling.Tests/ObjectLockingBudgetLiveTests.cs(live Postgres, gated onDARLING_TEST_PG) andLite.Tests/ObjectLockingBudgetTests.cs(local DuckDB, no gate needed). Both seed 200 rows across 10databases. Every row carries contention, so all 200 survive the read's filter. Both assert the default
call is under budget and marked
truncated. An explicitlimitcovering every seeded row returnseverything with
truncated: false./api/readrow forget_object_lockinginDarlingWebEndpoints.csnow pins its ownlimitat 200. That is the old effective ceiling (a hardcoded fetch with no override), not the new MCPdefault of 75. It is the same fix, and the same value, the neighboring
get_index_usagerow alreadycarries for an unrelated earlier default cut. 200 stays well under both
McpHelpers.MaxTopand thedispatch layer's own
MaxRowLimit(1000 each). The matching/api/catalogrow gained alimitparameterdeclaration. The viewer's
server-tabs.jspanel passes only{ server }, nolimit, so the row'sdefault governs. A new pin,
ObjectLockingWebDefaultTests, reads the row's own lines and fails if eitherdefault stops being 200. Lite parity: confirmed by grep that no Lite UI code calls
McpObjectStatsTools.GetObjectLockingdirectly. Lite's UI readsLocalDataService, not the MCP tools, sothis default change does not reach Lite's UI at all.
origin/dev. Merged, not rebased, to pick up the concurrentfix/4192-mcp-slow-toolslane. That lane had separately cutget_index_usage's own MCP default(
IndexUsageTop) from 200 to 75. Kept both constants at their own new value. Re-measuredMcpToolsListBudgetTests.TotalCeilingByteswith both changes applied, rather than adding the two PRs'deltas by hand: 171,797 bytes.
Test plan
Measured on my own seeded fixture: 200 rows across 10 databases, index/table names sized to reproduce the
field's ~356.7 bytes/row.
limitparam): 75,496 bytes, close to the field's71,332 B, confirming the fixture reproduces the reported shape. The test fails here, as expected, proving the
pin catches the old shape.
limit= 75): 28,801 bytes, under the 32,768-byte budget with about 12% headroom forindex/table names longer than this fixture's.
Darling.Tests, targeted classes while iterating (ObjectLockingBudgetLiveTests,DarlingMcpObjectStatsToolsTests,DarlingMcpObjectStatsToolsSurfaceAndSqlTests,McpToolsListBudgetTests,IndexUsageTruncationTests*), all green.Darling.Testsfull suite, after mergingorigin/dev: 13,844 total, 0 errors, 0 failed, 47skipped (unrelated gated/live-only tests), 1 not run. One param-contract failure on the first
run was fixed before this run.
Lite.Testsfull suite, run once: 5,340 total, 0 errors, 0 failed.LiteandDarling.Tests/Lite.Testsbuilds: 0 warnings each.cross-check.
Darling.Tests, filtered to every class that readsDarlingWebEndpoints.cs(55 classes, includingDarlingWebEndpointsTestsandServerPageTabsTests).Also filtered to
ObjectLockingWebDefaultTests,McpToolsListBudgetTestsandMcpPayloadContractCensusTests. Result: 1,434 total, 0 failed, 31 skipped (live classes, no rig for thisupdate). Also ran
DarlingMcpObjectStatsToolsTestsalongside the budget and census classes: 73 total, 0failed. Proved the new pin fails by reverting the
limitargument alone: 1 run, 1 failed,Assert.Containson the missing substring. Restored the fix and reran the full filtered set green.CHANGELOG entry
SECTION: Fixed
ENTRY:
get_object_lockingdefault response stays under the MCP budget ([MCP budget: size get_object_locking's default rows (#4198) #4258]): Before this fix, the tool returned up to 200 locking rows with no row limit. On a busy server it pushed 71 KB, and some MCP clients refused the response. It now returns 75 rows by default, highest contention first. It reportstruncated/objects_returnedwhen there are more. Passlimitfor more rows, up to 1000.REF:
[MCP budget: size get_object_locking's default rows (#4198) #4258]: MCP budget: size get_object_locking's default rows (#4198) #4258
Generated with Claude Code
https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ