fix(mcp): keep an explicit projectPath project in sync (#1835) - #1886
Open
danusha2345 wants to merge 1 commit into
Open
danusha2345 wants to merge 1 commit into
danusha2345 wants to merge 1 commit into
Conversation
…1835) A project opened through a tool call's `projectPath` (a repository other than the server's default — e.g. an indexed child of an un-indexed workspace) was opened read-only: no catch-up sync on open and no file watcher, so its answers went stale until someone ran `codegraph sync`. The engine now owns the lifecycle of every project the ToolHandler opens for an explicit path, mirroring the default project: - the project's writer lock is acquired (colbymchenry#1740 single-writer rule); if another live process holds it, that process keeps syncing and we only read; - a catch-up `sync()` runs on open and the first call against that project awaits it, time-boxed like the default gate (colbymchenry#905); - a file watcher runs for as long as the project stays cached. Bounds: the cache is keyed by the canonical (realpath) root, so a symlinked spelling shares one connection and one watcher; it is LRU with `MAX_CACHED_PROJECTS = 8` — opening a ninth closes the least recently used (watcher stopped, writer lock released, DB closed); `stop()` closes them all. Un-indexed paths still get the success-shaped guidance. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
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.
Symptom
A single MCP server started in an un-indexed workspace whose children each have their own
.codegraph/(the multiple-indexed-children case from #1606/#1607) can query each child throughprojectPath, but those projects were opened read-only: no catch-up sync on open and no file watcher. Renaming or adding an exported function in a child and querying it again after the debounce window returned the old index until someone rancodegraph syncby hand. The same applied to anyprojectPathother than the server's default project.Fix
The engine now owns the lifecycle of every project the
ToolHandleropens for an explicitprojectPath, mirroring what the default project already gets:writer.pidis acquired; if another live process (its own shared daemon, another direct-mode session) holds it, that process keeps syncing and this session only reads — no contention oncodegraph.lock, andstop()leaves the foreign lock untouched.sync()runs when the project is first opened, and the first tool call that names that project awaits it, time-boxed exactly like the default project's gate (v1.0.1, serve --mcp --no-watch -p <monorepo>, after container restart, the first tools/call is unresponsive; CODEGRAPH_NO_DAEMON=1 can reproduce the recovery process. #905). Edits made while no server was running are visible on the first call.watch()runs for as long as the project stays in the cache, with the same debounce / degraded handling as the default project (the callbacks are now one sharedwatchOptions()in the engine).Nothing changes for un-indexed paths: they still get the success-shaped "not indexed" guidance, never
isError, and are never indexed.Bounds
MAX_CACHED_PROJECTS = 8. Opening a ninth distinct project closes the least recently used one: watcher stopped, writer lock released, DB closed. ManyprojectPathvalues leak nothing; a re-visit pays a reopen plus a catch-up.MCPEngine.stop()closes every cached project and releases every lock it took.Verification
npx tsc --noEmit -p tsconfig.json— exit 0.__tests__/mcp-projectpath-lifecycle.test.ts(3 tests, engine at an un-indexed workspace with two indexed children, driven throughMCPEngine.getToolHandler().execute): a pre-start edit is caught up on the firstprojectPathcall and a later edit is picked up by the watcher; three spellings of one root (direct, symlink, sub-path) open one watched instance whosewriter.pidexists and is gone afterstop(); a project whose lock a foreign live PID holds is served read-only and its lock is left in place.mcp-unindexed(8),mcp-stale-slice(6),mcp-roots(3),watcher(36),mcp-catchup-gate(7),mcp-staleness-banner(7),writer-lock(6),mcp-writer-lock(2).Fixes #1835
🤖 Generated with Claude Code