Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(chat): stop tool schemas from filling the Claude harness context window

merged#2920CedarCopilot

CedarCopilot wants to merge 2 commits into staging from worktree-send-it-chat-harness-tool-deferral

What Libra verified

  • A pending MCP server remains discoverable during its initial chat turn

    Libra ran this behavior against the change and confirmed the check detects when it breaks.

    Technical evidence
    • the initial chat prompt identifies a configured pending MCP server so the model can look for its tools during the same turn
    • the initial chat tool allowlist enables ToolSearch and the pending server's wildcard tools during the same turn

    Changed code: delegate-tool-scoping.ts.

  • A rep's research request returns results

    Libra ran this behavior against the change and confirmed the check detects when it breaks.

    Technical evidence
    • a rep’s research request has WebSearch and WebFetch available so the agent can return a result instead of failing
    • a rep’s research request keeps each connected research server reachable instead of failing because its tools are omitted
  • Bound agent threads retain only their granted MCP servers

    Libra ran this behavior against the change and confirmed the check detects when it breaks.

    Technical evidence
    • the bound turn's allowed-tool wildcards include the granted server but not an ungranted user server
    • the bound turn's connected-server prompt line names only the granted MCP server
    • the bound turn's server-derived surfaces stay aligned with the same narrowed grant

    Changed code: delegate-tool-scoping.ts.

  • Deferred MCP tools do not preload every server schema into chat context

    Libra ran this behavior against the change and confirmed the check detects when it breaks.

    Technical evidence
    • ToolSearch is available for connected MCP servers while Cedar, native, and subagent tools remain available
    • Cedar, native, and subagent tools remain available when no user MCP servers are connected

    Changed code: delegate-tool-scoping.ts.

Live on prod, watching, 2 days leftTimeline and evidence
  1. Opened
    Sep 23, 2026, 12:53 PM
  2. Sep 23, 2026, 1:13 PM
  3. Merged
    Sep 23, 2026, 1:41 PM
  4. Live on prod
    Sep 23, 2026, 1:41 PM
  5. Observed 0 hours
    Sep 23, 2026, 1:41 PM
  6. Watching

    Live on prod, watching, 2 days left

    Sep 23, 2026, 1:41 PM
  7. Pipelines steady after this deploy
    Sep 23, 2026, 1:41 PM

Behaviors Libra is checking

The chat harness emits privacy-preserving diagnostics for tool availability, MCP server readiness, first-request context size, and every compaction without allowing telemetry failures to abort the chat.Not checked
prod
The chat harness keeps ToolSearch available and stops force-loading connected user MCP servers, so MCP tool schemas are fetched on demand instead of filling the Claude context and triggering autocompaction interruptions.Not checked
prod
When a granted external MCP server is still connecting at session initialization, the system prompt names that server and directs the model to use ToolSearch so its tools remain discoverable on demand in the same turn without exposing servers outside the grant-narrowed set.Not checked
prod

Libra has verdicts on 0 of 3 tracked behaviors on prod; 3 are still being checked. Libra checks hourly for 3 days after each deploy.

Summary

  • The Claude chat harness started every turn with ~194k of a 200k context window taken by tool schemas, so any turn with a few tool calls hit the SDK's "Autocompact is thrashing" guard and showed users "Agent Interrupted". Two causes: ToolSearch was missing from options.tools (the SDK restricts built-ins to the names listed, so tool-search deferral was silently off and all MCP schemas loaded every turn), and alwaysLoad: true on every user MCP server pinned their schemas into context even with deferral on.
  • Fix: name ToolSearch in the allowlist (new pure, tested buildAllowedTools) and drop alwaysLoad from user MCP server configs. Tools are now discovered on demand.
  • Because a user MCP server still pending at the start of a turn is not in the deferred list, the harness system prompt now carries one extra line naming the user's configured servers and telling the model to use ToolSearch for them (sorted and deduplicated so the prompt is byte-stable for caching, names over 64 chars dropped, capped at 20, no line at all when there are no servers).
  • Adds per-turn context telemetry (below) so the baseline size, missing deferral, not-ready servers and any compaction are visible in Axiom, plus Sherlock knowledge entry #47.

Test plan

  • Gates: check-brand-assets, oxlint --deny-warnings, pnpm deps:check (no violations), pnpm run types (apps/server) all clean. Not run: full pnpm run autofix:local (it ends in an unscoped vitest run src/ and the whole mail Jest suite, which this repo's hygiene rules forbid).
  • Scoped vitest: src/mastra/workflows/chat (14 files, 169 tests) passes after merging staging. New/changed tests: context-telemetry.test.ts (summarizers and the observe closure incl. never-throws), delegate-tool-scoping.test.ts (buildAllowedTools includes ToolSearch; the connected-servers line: sorted, deduplicated, byte-identical across input orders, cap at 20, long/reserved names dropped, single line, unchanged prompt for no servers), user-mcp-servers.test.ts (alwaysLoad absent on every server config).
  • Battle-tested live on the real code (local chat-service, real model, persistence off, read-only prompts), A/B against the current config:
    • /context baseline for a Notion-connected user: 193.9k (97%) -> 23.5k (12%); a no-external-MCP control 131k -> 25k.
    • A turn shaped like the failing prod one (find-crm-conversations -> fetch-conversation -> Read -> Grep): current config reproduced the exact Autocompact is thrashing error (3 compactions, 190s); with this change it completes, 0 compactions, first request ~42k tokens (re-verified on the final commit).
    • On a natural prompt ("check my Notion for the most recently edited page") the model goes straight to ToolSearch then the Notion tool; a server reported pending at init was still discovered in the same turn and was listed as configured from the prompt line.
  • Thermo review: 3 fresh-agent rounds on the main change plus a fresh review of the prompt-line commit; every finding fixed or explicitly justified.

Verify in prod

Log lines from run-chat-agent-sdk.ts (emitted once per Claude-mode chat turn):

  • Axiom dataset: cedar-prod-logs (also cedar-staging-logs) , query:
    ['cedar-prod-logs'] | where _time > ago(1h) and message == "[run-chat-agent-sdk] first request context"
    | summarize p50=percentile(todouble(context_tokens), 50), p95=percentile(todouble(context_tokens), 95), max=max(todouble(context_tokens)) by bin(_time, 10m)
    
    Other lines: [run-chat-agent-sdk] tool baseline (tool_search_available, available_tool_count, available_mcp_tool_count, mcp_servers name+status; counts include deferred tools), [run-chat-agent-sdk] context compacted (trigger, pre_tokens), warns [run-chat-agent-sdk] tool search unavailable and [run-chat-agent-sdk] user MCP server not ready at init (servers name+status), [run-chat-agent-sdk] context telemetry failed.
  • CloudWatch: `/aws/ecs/aws-pr
Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/server/.claude/skills/playbook-instruction-eval/write-document-bash-competition-eval.tsno production surface mapped
  • apps/server/.claude/skills/sherlock/SHERLOCK_SYSTEM_KNOWLEDGE.mdno production surface mapped
  • apps/server/src/mastra/tools/family-tool-names.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/harness/__tests__/context-telemetry.test.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/harness/__tests__/delegate-tool-scoping.test.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/harness/__tests__/user-mcp-servers.test.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/harness/context-telemetry.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/harness/delegate-tool-scoping.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/harness/types.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/harness/user-mcp-servers.tsno production surface mapped
  • apps/server/src/mastra/workflows/chat/run-chat-agent-sdk.tsno production surface mapped