fix(hooks): respect subagent ctx tool availability#834
Conversation
|
Maintainer-style validation report for the final PR head I validated this as a core hook-routing change, not only as a build/typecheck check. Claim / behavior checkedThis PR is intended to fix two Claude Code hook-routing behaviors:
The important invariant is that this must not weaken the normal main-session protection:
Git archaeology / root causeI checked the relevant history before accepting the fix shape:
The root cause is that Before / after behaviorUsing an upstream/next comparison harness: Before this PR:
After this PR:
Regression coverageCoverage was integrated into the existing owner file The final test coverage asserts:
I also ran an independent Claude Haiku adversarial review against the final diff. It found no blockers. The suggested extra tests were the default Agent injection check, WebFetch in the MCP-backed redirect batch, and omitted-options default behavior; those were added in 3OS validationFinal commit under test: Linux local:
macOS via
Windows via
The direct hook smoke on all three OSes verified the actual hook process contract, not only
Broad-suite noteI also ran the broader local Vitest suite. It did not pass cleanly, but the failures were separated from this PR:
I did not treat the broad-suite failure as a pass, but I also do not see evidence that those failures are introduced by this PR. ConclusionThe #794 / #832 behavior is confirmed, the root cause is history-backed, and the final patch preserves the older #683, #725, and #233 invariants. I consider this merge-worthy after the final |
What / Why / How
Fixes #794.
Fixes #832.
Claude Code subagent-originated
WebFetchcalls can be blocked by the main-session MCP readiness check even when that subagent cannot invoke anyctx_*MCP tools. In that state the hook deniesWebFetchand tells the subagent to callctx_fetch_and_index, but the suggested tool is not available in the subagent's tool surface.Claude Code
Agentprompt injection also had no supported opt-out. The default injection is useful for subagents that can use context-mode tools, but foreground subagents can hang or become unusable when forced intoctx_*calls that are unavailable or blocking.With this PR applied, Claude Code PreToolUse distinguishes machine-global MCP readiness from the current caller's
ctx_*availability:WebFetchstill redirects toctx_fetch_and_indexWebFetchpasses through when the hook payload hasagent_idoragent_typeAgentprompt injection remains unchangedCONTEXT_MODE_DISABLE_AGENT_INJECTION=1disables onlyAgentprompt injection<ctx_commands>remains omittedtests/hooks/core-routing.test.tsREADME.mdAffected platforms
Agent / CLI scope:
OS scope:
Scope notes:
agent_id/agent_typediscriminator is Claude Code-specific, so other adapters are not changed to infer subagent tool availability.routePreToolUse()keepsmcpToolsAvailabledefaulting totrue, so existing callers preserve the current redirect behavior unless an adapter explicitly marks the caller as unable to invokectx_*.CONTEXT_MODE_DISABLE_AGENT_INJECTIONopt-out affectsAgentprompt injection only. It does not disable SessionStart routing guidance, MCP redirects in the main session, or the context-mode MCP server.Root Cause
Git archaeology:
9e29a94/ PR fix(server): comprehensive ctx_* tool description audit + WebFetch refusal (substitutes #654) #683 introduced the WebFetch redirect/refusal path and the current "redirect, not restriction" wording.b1ba5cc/ PR fix(routing): bootstrap deferred ctx_* tools in subagent prompts (#724) #725 added the Claude Code ToolSearch bootstrap to subagent prompts so deferredctx_*schemas can be loaded before use.245cd8a/ PR Skip ctx_commands injection in subagent sessions #233 made subagent routing blocks omit<ctx_commands>, because subagents cannot use meta commands such as stats/doctor/upgrade/purge.The failure happens because
isMCPReady()is machine-global. It answers whether a context-mode MCP server process is alive on the machine, not whether the current Claude Code caller context can invokectx_*tools. Claude Code PreToolUse payloads includeagent_id/agent_typefor subagent-originated calls, so the adapter can pass that caller context into shared routing.The PR preserves these older invariants:
ctx_fetch_and_indexAgentprompt injection remains enabled<ctx_commands>omission remains in subagent routing blocksRelated prior work checked:
next.Agentprompt injection.Cross-agent / cross-OS risk
This PR is intentionally scoped to Claude Code's hook adapter because
agent_idandagent_typeare Claude Code PreToolUse payload fields. The shared routing API keeps the previous default for all other adapters, so Cursor, VS Code Copilot, JetBrains Copilot, Gemini CLI, Qwen Code, OpenCode/Kilo, Codex CLI, OpenClaw/Pi, Kiro, Antigravity, Zed, and standalone MCP launchers do not get new subagent tool-availability behavior unless their adapter explicitly opts in later.The runtime code is plain Node.js hook code and was validated on Linux, macOS, and Windows. No platform-specific path handling, shell parsing, or generated server bundle path is changed.
Validation
Detailed maintainer-style validation results are reported in a PR comment rather than kept in the body.