Skip to content

Load MCP tools on demand; cap derived summary trigger at 100k - #304

Merged
regnull merged 2 commits into
mainfrom
feat/lazy-mcp-tools
Oct 18, 2026
Merged

regnull merged 2 commits into
mainfrom
feat/lazy-mcp-tools

Conversation

@regnull

@regnull regnull commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Why

A week of runs measured: each engineer call sent ~67k prompt tokens on average, starting at ~52k before any work. About 45k of that is tool definitions, and Linear's 76 MCP tools alone are ~30k. Every bot carries them on every call, yet Linear was called 19 times out of ~3,200 tool calls. The filesystem MCP tools were never called.

Separately, since #299 the derived thresholds for glm-5.3-flash (1.05M window) are summary 200k / clear ~133k, so calls passed 170k tokens before anything was trimmed.

What

  • Lazy MCP tools (runtime/tool_loading.py)
    • MCP tools (registry source mcp:*) stay bound to the agent, but DeferredToolsMiddleware leaves them out of each model request until the bot loads them.
    • New load_tools tool: exact names (the server prefix can be dropped when unique) or a keyword query; at most 15 per call.
    • The system prompt gets a "Tools to load on demand" section listing names by server, with no schemas.
    • The loaded set lives in agent state (a union reducer), so it survives summarization and clearing; each run starts empty because the checkpoint is per run.
    • Loaded tools are appended after the always-on tools, so a load doesn't break the cached prefix of earlier definitions.
    • A call to an unloaded tool is refused and the tool is loaded, so the retry sees the real schema instead of guessed arguments.
    • Skipped for CLI-agent bots. defer_mcp_tools (default true) turns it off; it's in Settings → Context and the README.
  • MAX_DERIVED_SUMMARY_TRIGGER 200k → 100k: 1M-window models now summarize at 100k and clear at ~67k, still above the ~47k that caused re-reading before Cap the output reserve and derived summary trigger in context thresholds #299.

Expected effect: per-call tool definitions go from ~45k tokens to ~4k plus a ~1k name list, until a run loads something.

Tests

  • tests/test_tool_loading.py: search and name resolution, the prompt section, tools hidden until loaded and kept after, load by query, refuse-and-load, unknown names, the off switch.
  • The fake model now records the tools offered on each call.
  • Two threshold tests updated for the 100k cap.
  • Backend: 798 passed; ruff clean.

Not yet run against a real model; worth watching the first run that touches Linear.

🤖 Generated with Claude Code

regnull and others added 2 commits October 18, 2026 10:06
Every model call re-sent every bound tool definition. All four bots carry Linear's 76 MCP tools
(~30k tokens) plus Serena and filesystem (~11k more), on every call, while Linear was called 19
times in ~3,200 tool calls over a week.

- MCP tools stay bound but are left out of model requests until the bot loads them with the new
  load_tools tool (exact names or keyword query, max 15 per call); the system prompt lists them by
  name. The loaded set lives in agent state for the rest of the run; loaded tools go last in the
  tool list so a load keeps the cached prefix. Calling an unloaded tool is refused but loads it.
- defer_mcp_tools setting (default on) turns it off.
- MAX_DERIVED_SUMMARY_TRIGGER 200k -> 100k: on glm-5.3-flash's 1M window nothing was cleared
  until ~133k of messages and calls passed 170k tokens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@regnull
regnull merged commit 25a9f30 into main Oct 18, 2026
5 of 6 checks passed
@regnull
regnull deleted the feat/lazy-mcp-tools branch October 18, 2026 14:17
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