feat(mcp): deferred MCP tool loading — name-only stubs + tool_search activation (#119)#173
Merged
Merged
Conversation
added 3 commits
July 13, 2026 10:40
…SurfaceForRole split (#119)
… activation (#119) Adapter integration for deferred loading. resolveWorkerTools/resolveReviewerTools only fill fixed slots, so every other MCP tool was silently dropped; now the surplus is mounted: - mountDeferredTools splits the role's surplus (McpClientManager.toolSurfaceForRole) into directly-mounted (full schema, at/below threshold) vs. deferred (name-only + guard, above it), plus tool_search and a prompt index block when anything defers. - withActiveTools composes activeTools onto the #102 compaction prepareStep — one function returns both activeTools (every step, recomputed as activations accumulate) and the compaction messages override. Below threshold the plain compaction step is kept, so the surface is byte-identical for that role. - Worker + Reviewer wired; Planner (read-only) and ci-fix (fixed record) untouched per the issue. Fixed-slot-named MCP tools stay partial-filled, never deferred. Tests: mountDeferredTools split + fixed-slot exclusion, activeToolNames gating, and an over-threshold end-to-end (surplus reaches the Worker name-only + tool_search, deferred schemas absent from active tools). docs/mcp.md documents mcpDeferToolsOver. Closes #119.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (10)
📝 WalkthroughWalkthroughAdds configurable deferred MCP tool loading. Surplus tools are exposed through a searchable index, activated per subagent invocation, guarded before activation, and integrated into Worker and Reviewer tool preparation with configuration and test coverage. ChangesDeferred MCP tool loading
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Worker
participant McpClientManager
participant tool_search
participant DeferredMCPTool
Worker->>McpClientManager: request role tool surface
McpClientManager-->>Worker: direct tools and deferred tools
Worker->>tool_search: search or select deferred tool
tool_search-->>Worker: schema and activation
Worker->>DeferredMCPTool: call activated tool
DeferredMCPTool-->>Worker: tool result
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Contributor
Author
|
@coderabbitai full review |
✅ Action performedFull review finished. |
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.
What & why
Closes #119.
McpClientManagerfetches every configured server's tool schemas, but the adapter then narrows the union to fixed slots (readFile,bash, …) — so every other MCP tool a server exposes was silently dropped. Spreading the whole registry into each agent instead would serialize every tool's JSON schema into every request, on every step. This mounts the surplus behind deferred loading: name-only until fetched, so a large MCP registry becomes viable without the context-window tax.How
Three layers (one commit each):
mcpDeferToolsOverconfig key (int ≥ 0, default 20,0= always defer; project > global,pickhonors a configured0).McpClientManager.toolSurfaceForRole(role)returns{ direct, deferred }: at/below the threshold the whole surplus isdirect(byte-identical to plain mounting), above it the whole surplus isdeferred.tool_searchmechanism (src/mcp/tool-search.ts) —toolSearch(deferred)builds the tool + a per-invocation activationSet+ a name-only index block.select:name1,name2fetches exact names; any other query is keyword-ranked over names + descriptions (capped bymax_results, default 5); each match returns its full schema and activates.guardDeferredwraps a deferred tool so a pre-activation call returns a typed "fetch it viatool_searchfirst" result (never a throw / provider validation error).deferredToolIndexBlockrenders the always-visible tier.src/loop/run-loop-adapter.ts) —mountDeferredToolssplits the role's surplus (excluding fixed-slot-named MCP tools, which stay partial-filled), mounts direct surplus in full and deferred surplus guard-wrapped +tool_search, and appends the index block to the system prompt.withActiveToolscomposesactiveToolsonto the feat(subagents): wire Compactor into subagent loops via prepareStep — summarize-and-continue #102 compactionprepareStep— one function returns bothactiveTools(recomputed each step as activations accumulate) and the compactionmessagesoverride. Wired for Worker + Reviewer; the Planner (read-only survey) and CI-fix session (fixed record) are untouched per the issue.Scope boundaries (from the issue)
Deferred names use the
mcp__<server>__<tool>namespacing from #115. Activation is scoped to one subagent invocation (a fresh invocation starts fully deferred).submitand fixed local slots are never deferred. Below the threshold there is notool_search, no index block, and the role keeps its plain compactionprepareStep. Extending activation to the Planner / ci-fix is a separate follow-up.SDK notes
PrepareStepResult.activeToolsisArray<keyof TOOLS>; the composed step casts the computed name list at each concrete call site (Worker/Reviewer).prepareStepmessagesoverride does not persist across steps under Node —activeToolsis recomputed every step regardless, so activation state survives a compaction.Tests / gate
mcp-client.test.ts:toolSurfaceForRolesplit at/above/0threshold + allowlist interaction.tool-search.test.ts:select:/keyword/no-hit, schema rendering, index first-sentence, guard pre/post activation.config-loader.test.ts: default 20 + project-over-global + configured0honored.run-loop-adapter.test.ts:mountDeferredToolssplit + fixed-slot exclusion,activeToolNamesgating, and an over-threshold end-to-end (surplus reaches the Worker name-only +tool_search, deferred schemas absent from active tools; regression: tools beyond the fixed slots now reach the Worker).typecheck(both) +typecheck:tests(compat) +test:node(aitm 902 / compat 250) + biome all clean.Part of #100.
Summary by CodeRabbit
New Features
tool_search.Documentation