Skip to content

Darling and Lite's MCP server instructions drop from ~86KB/52KB to under 8KB each (#3898) - #4039

Merged
erikdarlingdata merged 3 commits into
devfrom
fix/3898-mcp-instructions-phase2
Sep 23, 2026
Merged

erikdarlingdata merged 3 commits into
devfrom
fix/3898-mcp-instructions-phase2

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Refs #3898.

Why

#3898 measured the MCP surface at ~100k tokens of context before the first call: 336 KB of tools/list
(157 tools) plus 86 KB of server instructions. Erik's ruling adopted D1-D10; Phase 2 is the core-instructions
cut (D5): the instructions drop their per-tool "Tool Reference" tables and prose, because a tool's own
description in tools/list is the authoritative surface for what it does and what it takes, and nothing
enforced that a duplicated copy in the instructions stayed in sync with it (#3696, #3821 both drifted). D6
requires Lite to move in lockstep with Darling, no Lite-only profile.

A previous lane (WIP on this branch, commit 77b48e0d) did the bulk of this work and stopped at its deadline
before its full suite reported. This PR merges origin/dev, verifies the phase against the checklist below,
fixes what verification found, and runs both full suites.

What changes

Instructions (Lite/Mcp/McpInstructions.cs, Darling/.../Mcp/DarlingMcpInstructions.cs): the per-family
Markdown tables and prose (wait-type-to-tool mapping, tool relationships, the numbered "Recommended Workflow",
per-tool descriptions) are gone. What stays: the read-only preamble, the collection-cadence note, a one-line
tool census, the status envelope vocabulary (empty/unavailable/not_collected/precondition/error/invalid), the
as_of/hours_back anchor rule, and a short "Workflow" arrow-chain. Darling additionally keeps its
PostgreSQL-wait-instrument-tiers paragraph and the partial-update reminder for update_alert_settings /
update_mute_rule / Custom Views (no Lite equivalent tools exist, so no parity gap).

Content-reachability verification (the main risk in this phase). I checked every removed block against
where its content had to end up, per file:

  • get_collection_health's analysis_caveats clause -> moved into the tool's own description
    (DarlingMcpDataTools.cs / Lite/Mcp/McpHealthTools.cs), pin updated
    (CollectionCaveatLedgerTests.BothDescriptions_CarryTheAnalysisCaveatsClause_WithoutASplice).
  • get_ag_health's per-replica-column trap (operational_state/recovery_health LOCAL-only,
    connected_state primary-only) -> moved into DarlingMcpAgTools.cs's own description, pin updated
    (DarlingMcpAgToolsSurfaceTests).
  • get_blocking/get_deadlocks/get_deadlock_detail's dedup_key display-name scoping -> moved onto the
    parameter's own [Description], pin updated (DarlingMcpBlockingToolsTests).
  • get_sweep_reports's mute semantics / "at most one daily rollup" cadence fact -> moved into
    DarlingMcpFleetSweepTools.cs's description, pin updated.
  • get_query_store_clutter's capture-mode facts -> moved into DarlingMcpQueryStoreClutterTools.cs, pin
    updated.
  • add_servers/remove_server/mute_analysis_finding/compare_analysis/the plan tools' create_statement
    caveat -> each was already fully stated in the tool's own description; the instructions row was pure
    duplication, safe to cut outright. Pins updated to check only the description.
  • The wait-type-to-tool mapping table, the "Tool Relationships" bullet list, and the numbered workflow -> no
    replacement.
    These are discovery/workflow shortcuts, not facts a tool needs to be used correctly, and
    cutting them matches the design intent (D5's "one-line census, not a re-description"). Judgment call, flagged
    for the coordinator.

Found and fixed in this PR (not in the WIP's original diff):

  1. get_memory_pressure_events had a dangling pointer. Lite's tool description ended with "...see the
    'Interpreting Memory Pressure Events' section of the server instructions" for "actionable interpretation and
    suggested follow-up tools" - but this same phase renamed that section to "Memory pressure signals" and
    deleted the per-signal follow-up-tool tables and the "Patterns" section it promised. Darling's twin
    (DarlingMcpMemoryGrantTools.cs) never had this pointer; its description was already self-contained. Fixed
    by inlining the follow-up guidance directly into Lite's description (Lite/Mcp/McpMemoryTools.cs): check
    get_memory_grants/get_memory_clerks for process pressure, get_server_properties for system-only
    pressure. No test pinned the old text, so nothing else needed updating.
  2. A pin test regressed unrelated to The MCP surface costs ~100k tokens of context before the first call: 157 tools, 336 KB of tools/list, 86 KB of instructions #3898. The WIP's McpDescriptionTruthPinTests.cs rewrote
    GetCpuUtilization_NamesBothSourceCadences_AndNotAFifteenSecondRingBuffer to expect an inline
    note = "..." literal in each SKU's own get_cpu_utilization body. Both SKUs still build that payload
    through the shared TrendPayloads.CpuUtilization / CpuCadenceNote constant (The rest of the trend family still returns every collection: a week of get_tempdb_trend is 2 MB, get_memory_trend 1.5 MB, get_wait_trend 1.1 MB #3960) - production code was
    never touched, so the rewritten pin failed against both files. Reverted to the original assertion, which
    reads the one shared constant.
  3. Another pin test regressed unrelated to The MCP surface costs ~100k tokens of context before the first call: 157 tools, 336 KB of tools/list, 86 KB of instructions #3898. DarlingMcpDataToolsTests.ServerListSql_EnabledServers_WithLastCollection
    had reverted from list_servers and the viewer's last-collection reads plan every retained collection_log chunk: ~170 ms of cold planning on 19 chunks, several times that at 60 #3976's LEFT JOIN LATERAL freshness probe back to a stale MAX(cl.collection_time)
    expectation. DarlingDataReader.ServerListSql was never touched by the WIP and still uses the LATERAL
    probe; restored the matching assertions.

Root cause for (2) and (3) isn't obvious from the WIP's history (neither file relates to #3898's actual scope);
flagged for the coordinator in case it points at a tooling issue worth a census.

Measured

Instructions, actual runtime .Length (via reflection against the built assemblies), before = origin/dev,
after = this branch:

SKU Before After Change
Lite (McpInstructions.Text) 52,942 7,721 -45,221 (-85.4%)
Darling (DarlingMcpInstructions.Text, no peers) 88,532 7,992 -80,540 (-91.0%)

Both are now under D2's 8,000-character instructions budget, pinned by the new
*McpInstructionsBudgetTests. Darling has only 8 characters of headroom - worth flagging for whoever
touches these instructions next (Phase 4's /core work included).

tools/list (this phase's scope is instructions, not tool descriptions, but three tools gained content that
migrated out of the instructions - blob-to-blob byte size via git show, so line-ending-neutral):

File Before After Change
DarlingMcpAgTools.cs 5,943 6,137 +194
DarlingMcpFleetSweepTools.cs 11,600 11,765 +165
Lite/Mcp/McpMemoryTools.cs 23,566 23,606 +40

Total tools/list growth: ~399 bytes across both SKUs combined - negligible against the instructions savings.
Tool count is unchanged (Lite 88, Darling 158, 87 shared, 1 Lite-only (get_blocked_process_reports, which
Darling exposes as get_blocking) - all verified by direct reflection count, matching the census sentences in
both instructions texts exactly).

Test plan

  • dotnet build Lite.Tests/Lite.Tests.csproj -c Debug - Build succeeded, 0 Warning(s), 0 Error(s)
  • dotnet build Darling/Darling.Tests/Darling.Tests.csproj -c Debug - Build succeeded, 0 Warning(s), 0 Error(s)
  • Targeted classes, Lite.Tests (McpInstructionsBudgetTests, McpDescriptionTruthPinTests,
    CompareAnalysisDispersionTests): Total: 9, Failed: 0
  • Targeted classes, Darling.Tests (DarlingMcpInstructionsBudgetTests,
    AnalysisFactsReadRunsDetectorParityTests, CollectionCaveatLedgerTests,
    CollectionCaveatLedgerParityTests, DarlingMcpAgToolsSurfaceTests,
    DarlingMcpBlockingToolsSurfaceAndSqlTests, DarlingMcpDataToolsSurfaceAndSqlTests,
    DarlingMcpFleetSweepToolsTests, DarlingMcpPlanToolsSurfaceAndSqlTests,
    DarlingMcpServerAdminToolsSurfaceTests, DarlingMcpToolsTests, DarlingPeerDisclosureTests,
    QueryStoreClutterTests): Total: 316, Failed: 0, Skipped: 1
  • Full Lite.Tests suite, once: Total: 5182, Failed: 0, Skipped: 0
  • Full Darling.Tests suite, once: Total: 13039, Failed: 0, Skipped: 613 (all live-PostgreSQL-gated)
  • Live-PostgreSQL classes (DarlingMcpAgToolsLivePostgresTests, DarlingMcpBlockingToolsLivePostgresTests,
    DarlingMcpDataToolsLivePostgresTests, DarlingMcpPlanToolsLivePostgresTests,
    DarlingMcpServerAdminToolsLivePostgresTests, and the rest of the 613 skipped) - not run against a live
    rig.
    Nothing in this PR (the WIP's original diff or my three fixes) touches PostgreSQL-dependent
    behavior; every change is instructions text or tool/parameter [Description] strings plus two
    test-assertion reverts against untouched production code. Judged low-risk given the deadline, but
    unchecked per the standing orders.
  • D9's misreading check (10-15 known traps) - that gate is scoped to "before the Phase 3 content PRs merge"
    per Erik's ruling, not Phase 2. Not run here; still pending for whoever opens Phase 3.

Issues filed

None. Both defects found during verification were small and in-lane (a dangling doc pointer, two reverted pin
assertions) and are fixed in this PR per the no-deferrals rule.

What the coordinator should double-check

  • Darling's instructions are at 7,992/8,000 characters - effectively no headroom left under D2's budget.
  • My fix to get_memory_pressure_events's description (inlining specific follow-up tools rather than pointing
    back at the instructions) is a judgment call on wording, not just mechanics - worth a read.
  • The two unrelated pin regressions in the WIP (CPU-utilization note, ServerListSql) had nothing to do with
    The MCP surface costs ~100k tokens of context before the first call: 157 tools, 336 KB of tools/list, 86 KB of instructions #3898's scope. I couldn't find an obvious mechanism for how they got into this branch; if this pattern shows
    up in other lanes' WIP it might be worth a census.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv

erikdarlingdata and others added 3 commits September 23, 2026 06:53
…l suite reported)

Instructions and tool-description cuts plus the two new instructions-budget
tests, as lane L left them. Unverified: its final full Darling run never
reported. A later lane or the coordinator finishes, verifies and opens the PR.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv
The prior WIP commit trimmed the MCP server instructions correctly, but
left two unrelated defects behind:

- Lite/Mcp/McpMemoryTools.cs: get_memory_pressure_events's own
  description pointed to the "Interpreting Memory Pressure Events"
  section of the server instructions for "actionable interpretation and
  suggested follow-up tools" - a section this same phase renamed and
  gutted. Inlined the follow-up-tool guidance (get_memory_grants /
  get_memory_clerks for process pressure, get_server_properties for
  system-only pressure) directly into the tool's own description
  instead, matching Darling's get_memory_pressure_events, which never
  pointed at the instructions to begin with.

- Lite.Tests/McpDescriptionTruthPinTests.cs: the WIP rewrote
  GetCpuUtilization_NamesBothSourceCadences_AndNotAFifteenSecondRingBuffer
  to expect an inline `note = "..."` literal in each SKU's own
  get_cpu_utilization body. Both SKUs still build that payload through
  the shared TrendPayloads.CpuUtilization/CpuCadenceNote (#3960), so the
  rewritten pin failed against current, unmodified production code on
  both SKUs. Reverted to the original assertion, which reads the one
  shared constant.

- Darling/Darling.Tests/DarlingMcpDataToolsTests.cs: the WIP's
  ServerListSql_EnabledServers_WithLastCollection assertion had reverted
  from #3976's LEFT JOIN LATERAL freshness probe back to a stale
  MAX(cl.collection_time) expectation, unrelated to #3898. Production
  code was never touched and still uses the LATERAL probe; restored the
  matching assertions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 23, 2026 13:32
@erikdarlingdata
erikdarlingdata merged commit df78179 into dev Sep 23, 2026
15 of 16 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/3898-mcp-instructions-phase2 branch September 23, 2026 13:32
erikdarlingdata added a commit that referenced this pull request Sep 23, 2026
…ntries in their sections (#4080)

Adds 42 entries and 42 link refs (#3992, #3995, #3996, #3998, #4001, #4002, #4003, #4007, #4010, #4011, #4013, #4015, #4020, #4022, #4025, #4029, #4030, #4031, #4036, #4038, #4039, #4040, #4044, #4047, #4048, #4049, #4050, #4051, #4055, #4061, #4063, #4064, #4065, #4066, #4067, #4068, #4069, #4070, #4071, #4073, #4074, #4078). Each PR's entry was buffered, and this lands every entry whose PR was merged on origin/dev when it ran.

#3989 left 26 entries under bare 'Changed' and 'Fixed' lines above '### Added'. They move into '### Changed' and '### Fixed', below the new entries, and one blank line stays under [Unreleased].


Claude-Session: https://claude.ai/code/session_01Ua31ugERL5DmhFVRtf6keQ

Co-authored-by: Claude Opus 5.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