fix(subagent): pass inherited MCP tools through to workers - #1296
Merged
Merged
Conversation
TheGreatAxios
force-pushed
the
fix/mcp-passthrough-3a1d5f87
branch
from
October 2, 2026 20:32
e9e3d30 to
027ea02
Compare
requires_tools entries for live inherited-MCP tools (mcp__<server>__<tool>) now validate against the parent's mounted set at dispatch instead of rejecting as unknown_tool, and the worker mount exempts inherited MCP tools from the built-ins-only allowlist strip. Unmounted mcp__ names still reject fail-closed, and explicit excludes still withhold live MCP tools.
TheGreatAxios
force-pushed
the
fix/mcp-passthrough-3a1d5f87
branch
from
October 2, 2026 20:38
027ea02 to
1fe2068
Compare
On-demand narrowing stripped inherited MCP tools unless stamped in requiresTools or named in an allowlist, breaking the inherit-by-default contract: workers inheriting via inheritMcpTools with a parent grant could no longer execute the inherited tool. Inheritance itself now counts as the on-demand request - run.ts passes the live inherited MCP set into applyCapabilityFilter, which retains those names when no narrower constraint (capabilities/requiresTools) applies. Stamped dispatches still narrow to the stamped subset, unmounted mcp__ names still drop fail-closed, and explicit excludes still withhold. The e2e probes also share worker id "worker" across fresh tmpdirs while denied-call grant envelopes live in a process-shared store, so one probe's pending envelope vetoed the next via the retry-from-another- directory blocker. withWorker now clears the process grant store per probe using the provided test-only seam. Also ignore scratch __repro/ dirs (port from main).
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.
Resolves CL-9704. Follow-up to CL-9721.
Problem
MCP tools never reached workers. Two layers stripped them, both fail-closed against the built-in catalog:
capability-preflight.ts: the static catalog loop returnedunknown_toolfor anymcp__*requirement.run.ts applyCapabilityFilter: planner/coder envelopes are allowlists of built-ins only (BUILD_TOOLS), so inherited Linear tools were dropped at mount even past preflight.CL-9704 (Linear MCP discoverable but not callable): the primary session already mounts connected MCP tools into the live registry and promotes them onto the wire on execute — this PR locks that discovery-to-invocation chain with an integration test (tool_search finds
mcp__linear__list_teams, promote-on-execute commits its callable schema and dispatches, thenmcp__linear__save_issuefollows the same path).Fix (on-demand, requires_tools-gated — not full auto-mount)
Workers do NOT mount every inherited MCP tool. An inherited
mcp__*tool mounts only when the dispatch explicitly requests it:capability-preflight.ts: newavailableMcpToolsinput carries the live inherited-MCP set; anmcp__*name present there skips the catalog/allowlist checks. Absentmcp__*names still reject asunknown_tool(never inferred from shape), get no did-you-mean hint, and explicit excludes still withhold live MCP tools aspermission_static. Preflight grants exactly the named requirement — a live sibling is never implied.run.ts applyCapabilityFilter: retains an inherited MCP tool only when stamped inrequiresToolsor named in an allowlistcapabilities.tools(intersection with the inherited set). Unrequested inherited MCP tools are dropped in every mode — allow, exclude, and full mount. An explicit exclude still withholds a requested live MCP tool (surfaces asstale_snapshotat the mount echo, normally pre-empted by dispatch preflight).agent-fleet.ts: dispatch threads the live inherited-MCP names (viadeps.inheritMcpToolsthrough the worker gate) into preflight, and stamps exactly the requested tools.mcp-promote-on-execute.test.tspins list_teams -> save_issue end to end against a mock Linear server.Caller note
A worker that wants an MCP tool must name it in
requires_tools(validated pre-spawn against the live inherited set). Dispatch withintent=plan/agent=plannerrather than general-purpose agents —registry.tsenvelopes are by design built-ins-only, and MCP reachability now flows through inheritance + explicit request. On primary, call a searchedmcp__*name and promote-on-execute commits its schema.Verification
bun testoncapability-preflight.test.ts+agent-fleet-requires-tools.test.ts+run-requires-tools.test.ts: 70 pass, 0 failbun testonmcp-promote-on-execute.test.ts+tool-search.test.ts+assemble-runtime.test.ts+exa-web-fetch-alias.test.ts: 95 pass, 0 failbun test src/subagent/: 752 pass, 0 failbun run typecheck: clean;bun run lint(oxfmt --check + oxlint): clean;bun run check:dead-exports: 0 violations