Problem
Claude (in Claude Code specifically) rarely uses CMM's graph/LSP tools (search_graph, trace_path, get_code_snippet, query_graph, get_architecture) for LSP-style questions (go-to-def, find-refs, call-chain, impact). It falls back to grep + Read instead. CMM's Hybrid-LSP call resolution is therefore largely unused, because Claude only benefits from it indirectly by calling those tools.
Observed live in a working session (the agent used grep/Read for all code navigation and made zero search_graph/trace_path/get_code_snippet/query_graph calls across a long session).
Root causes (evidence)
- CMM tools are deferred behind ToolSearch in the main context. Claude Code's Tool Search (
ENABLE_TOOL_SEARCH, default auto) defers tool schemas once they exceed ~10% of the context window. It is token-based, global, and all-or-nothing — there is no per-server / per-tool pinning. With ~11 MCP servers connected (~150+ tools) the threshold is crossed, so CMM tools are announced by name but must be ToolSearch-loaded before use. The extra hop means the always-loaded default (grep/Read) wins by inertia. (The CMM MCP server also disconnected mid-session at one point, making the tools outright unavailable.)
- Nothing redirects navigation off grep. CMM's own
session-gate.sh allow-lists Bash, Read, Grep, Glob (they bypass the gate). The gate blocks Edit/Write/WebFetch until indexing, but never redirects navigation. So no layer discourages grep-for-symbols.
- Persuasion ≠ behavior change. Tool descriptions ("use INSTEAD OF grep") and the MCP
instructions field are advisory. In-session evidence: the only thing that reliably changed tool selection was a hard PreToolUse block (context-mode's ctx-execute-enforcer redirecting bash → ctx_execute, complied with dozens of times). Persuasion lost every time; enforcement worked.
Key finding (the fix) — EMPIRICALLY TESTED
A subagent with a restricted tools: allow-list gets its CMM tools EAGERLY loaded — no ToolSearch, no deferral.
Test (this session): spawned a subagent whose definition restricts to ~20 tools including 8 granted mcp__codebase-memory-mcp__* tools. It reported:
- All 8 granted CMM tools present with full schemas immediately, no ToolSearch needed.
ToolSearch was not even in its toolset; no "deferred tools" reminder.
- ~20 tools total (curated set).
Conclusion: deferral is a main-context problem caused by aggregate tool count. A subagent restricted to a small toolset falls under the threshold and loads CMM eagerly. The restriction that scopes the subagent is exactly what un-defers its tools.
Supporting docs facts (via Claude Code docs):
- Subagents inherit the parent's tool definitions, filtered by the agent definition's
tools: allow-list; fresh context, no parent conversation. https://code.claude.com/docs/en/agent-sdk/subagents.md
SubagentStart hook exists and is context-only: it can inject additionalContext (plain-text advice) into the subagent, detected via agent_id/agent_type, but cannot control tools or load skills. https://code.claude.com/docs/en/agent-sdk/hooks.md
- Only the AgentDefinition controls tools (
tools:) and can preload skills (skills:).
- Whether restricted toolsets bypass deferral is undocumented — established here empirically, so re-verify across Claude Code versions.
Proposed solution
Ship a dedicated code-navigation subagent from this repo's installer (fits the existing feat/subagent-cmm-startup-hook direction):
---
name: cmm-navigator
description: Code navigation — definitions, callers, call chains, architecture.
tools: [mcp__codebase-memory-mcp__get_architecture,
mcp__codebase-memory-mcp__search_graph,
mcp__codebase-memory-mcp__get_code_snippet,
mcp__codebase-memory-mcp__trace_path,
mcp__codebase-memory-mcp__query_graph,
mcp__codebase-memory-mcp__search_code,
Read] # deliberately NO Grep/Glob — remove the fallback
skills: [codebase-memory-tracing]
---
Orient first: get_architecture -> search_graph -> get_code_snippet.
Use trace_path for callers/impact. search_code (not grep) for text search.
Rationale (each tied to evidence):
- Restricted
tools: -> CMM eagerly loaded (proven), deferral gone.
- Omit
Grep/Glob -> no lower-friction fallback; CMM is the path of least resistance (same principle as the enforcement that actually worked).
skills: preload -> AgentDefinition can preload a skill (hooks cannot).
- Optional
SubagentStart hook (this repo already has the plumbing) injects the orient-first decision table as additionalContext — reinforcement only.
Known gap / caveats
- This makes a CMM-first specialist reliably available and grep-free, but does not force the main agent to delegate to it. Pair with a main-session routing nudge ("route definition/caller/call-chain questions to
cmm-navigator") or invoke directly.
- Eager-load behavior is empirically established, not documented — re-verify across Claude Code versions.
- Don't over-gate
grep generally (string literals, configs, non-indexed files are legitimate grep uses); the point is to give navigation a CMM-only lane, not to break grep.
Acceptance criteria / next steps
Filed from a working session investigating why Claude wasn't using CMM's LSP features; all evidence above was gathered/tested in that session.
Problem
Claude (in Claude Code specifically) rarely uses CMM's graph/LSP tools (
search_graph,trace_path,get_code_snippet,query_graph,get_architecture) for LSP-style questions (go-to-def, find-refs, call-chain, impact). It falls back togrep+Readinstead. CMM's Hybrid-LSP call resolution is therefore largely unused, because Claude only benefits from it indirectly by calling those tools.Observed live in a working session (the agent used
grep/Readfor all code navigation and made zerosearch_graph/trace_path/get_code_snippet/query_graphcalls across a long session).Root causes (evidence)
ENABLE_TOOL_SEARCH, defaultauto) defers tool schemas once they exceed ~10% of the context window. It is token-based, global, and all-or-nothing — there is no per-server / per-tool pinning. With ~11 MCP servers connected (~150+ tools) the threshold is crossed, so CMM tools are announced by name but must beToolSearch-loaded before use. The extra hop means the always-loaded default (grep/Read) wins by inertia. (The CMM MCP server also disconnected mid-session at one point, making the tools outright unavailable.)session-gate.shallow-listsBash,Read,Grep,Glob(they bypass the gate). The gate blocksEdit/Write/WebFetchuntil indexing, but never redirects navigation. So no layer discouragesgrep-for-symbols.instructionsfield are advisory. In-session evidence: the only thing that reliably changed tool selection was a hard PreToolUse block (context-mode'sctx-execute-enforcerredirectingbash→ctx_execute, complied with dozens of times). Persuasion lost every time; enforcement worked.Key finding (the fix) — EMPIRICALLY TESTED
A subagent with a restricted
tools:allow-list gets its CMM tools EAGERLY loaded — no ToolSearch, no deferral.Test (this session): spawned a subagent whose definition restricts to ~20 tools including 8 granted
mcp__codebase-memory-mcp__*tools. It reported:ToolSearchwas not even in its toolset; no "deferred tools" reminder.Conclusion: deferral is a main-context problem caused by aggregate tool count. A subagent restricted to a small toolset falls under the threshold and loads CMM eagerly. The restriction that scopes the subagent is exactly what un-defers its tools.
Supporting docs facts (via Claude Code docs):
tools:allow-list; fresh context, no parent conversation. https://code.claude.com/docs/en/agent-sdk/subagents.mdSubagentStarthook exists and is context-only: it can injectadditionalContext(plain-text advice) into the subagent, detected viaagent_id/agent_type, but cannot control tools or load skills. https://code.claude.com/docs/en/agent-sdk/hooks.mdtools:) and can preload skills (skills:).Proposed solution
Ship a dedicated code-navigation subagent from this repo's installer (fits the existing
feat/subagent-cmm-startup-hookdirection):Rationale (each tied to evidence):
tools:-> CMM eagerly loaded (proven), deferral gone.Grep/Glob-> no lower-friction fallback; CMM is the path of least resistance (same principle as the enforcement that actually worked).skills:preload -> AgentDefinition can preload a skill (hooks cannot).SubagentStarthook (this repo already has the plumbing) injects the orient-first decision table asadditionalContext— reinforcement only.Known gap / caveats
cmm-navigator") or invoke directly.grepgenerally (string literals, configs, non-indexed files are legitimate grep uses); the point is to give navigation a CMM-only lane, not to break grep.Acceptance criteria / next steps
cmm-navigatoragent definition to the installer (restricted tools, no grep, skills preload).SubagentStarthook injecting the orient-firstadditionalContext.cmm-navigator.Filed from a working session investigating why Claude wasn't using CMM's LSP features; all evidence above was gathered/tested in that session.