The MCP server gated tool availability on whether the server root had a
.codegraph/ index, so in a monorepo where only sub-projects are indexed the
agent saw zero tools — and couldn't reach an indexed sub-project even by
projectPath. A session started before `codegraph init` also never surfaced the
tools afterward. The Claude front-load hook had the mirror gap: it only walked
UP for an index, so it stayed silent at a monorepo root.
MCP server:
- Always expose the tool surface; when the root isn't indexed, send a
per-project instructions variant (pass projectPath) instead of the
"inactive" note. Safety comes from response SHAPE (success-shaped guidance,
never isError), not from hiding tools.
- Reword the no-default-project guidance to be per-project, not per-session,
and sharpen the projectPath schema description.
Front-load hook (UserPromptSubmit):
- Scan DOWN (bounded depth, workspace-root-gated) for indexed sub-projects and
shape the injection by topology: front-load the one the prompt names, nudge
about the rest, or list them when ambiguous.
Verified: full suite (1703 passed); a live two-package monorepo run confirms the
hook front-loads the correct sub-project with no cross-package leakage. The
front-load's net speed effect is the existing multi-file-vs-single-file
tradeoff, unchanged by this work.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The spawn-based tests failed on Windows with EPERM in afterEach — the
SIGKILL'd server child briefly holds the temp cwd/SQLite handles when
rmSync runs (the documented class that fails mcp-initialize/mcp-roots
teardowns). Await the child's exit (3s cap) and retry the removal
(maxRetries/retryDelay); assertions were already passing on Windows.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
An MCP session in a workspace with no .codegraph/ previously got the full
"lean on codegraph for everything" playbook plus all 8 tools, then every
call returned isError — and one or two early errors teach an agent to
abandon codegraph for the whole session (maintainer-observed). Now the
initialize response picks an instructions variant by index state (cheap
sync walk-up, #172 respond-fast contract holds) and tools/list serves an
EMPTY list when unindexed: absence is the one signal an agent can't
misread. Indexing is deliberately the user's call — the inactive note
tells the agent not to run init itself.
No-error policy in the tool handler: expected/recoverable conditions
(NotIndexedError — cross-project query to an unindexed path, default-
project detection miss) return SUCCESS-shaped guidance instead of
isError; security refusals (PathRefusalError) stay hard errors without
retry encouragement; genuine internal failures keep isError but add a
retry-once note so a transient blip doesn't convert to permanent
abandonment. Principle recorded in CLAUDE.md.
Also: codegraph_search kind:"type" (advertised by its own schema enum)
silently matched nothing — now maps to type_alias; codegraph_explore's
query param no longer tells agents to run codegraph_search first
(contradicted explore's call-FIRST design); server-instructions
§Limitations rewords the unindexed case to stay-out-for-the-session.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>