fix(mcp): resolve ${CLAUDE_PROJECT_DIR} in overlay paths at runtime (#57, #58) - #60
Merged
Merged
Conversation
Claude Code does not expand ${CLAUDE_PROJECT_DIR} inside an mcpServers env
value -- ${VAR} expansion runs at config-parse time from Claude Code's own
environment, where the variable is unset, so the token baked by `starlog
init` reached the server literal. Every private overlay (corpus, facts,
policy) then failed its readFileSync, warned only to stderr (which the MCP
stdio channel discards), and silently degraded to public-only.
Claude Code does inject CLAUDE_PROJECT_DIR into the spawned server's
process.env, so the fix is a runtime resolver: new resolveOverlayPath()
expands ${VAR}/${VAR:-default} (CLAUDE_PROJECT_DIR falling back to cwd),
a leading ~, and project-relative paths, and is idempotent on absolute
paths. loadPrivateCorpus / loadPrivateFacts / loadPolicy call it at read
time, repairing already-wired installs with no re-init. The misleading
init.ts comment that claimed parse-time expansion is corrected.
doctor's "Private overlays wired" check reported ok on mere presence of
the env keys, so a stale absolute path or cross-project leak passed as
healthy -- it could not catch this class of bug. It now resolves each
wired path exactly as the server will (via resolveOverlayPath against the
known project root) and warns when it does not land on this project's
.starlog/.
Full suite green (577 tests); no package.json version bump (left for the
release step per RELEASING.md).
Closes #57
Closes #58
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Fixes the root-cause bug where private overlays wired with
${CLAUDE_PROJECT_DIR}silently returned public-only results, plus thedoctorblind spot that couldn't catch it. Closes #57 and #58.Root cause (#57)
starlog initbakes${CLAUDE_PROJECT_DIR}/.starlog/{private-facts,private-corpus,policy}.jsoninto the MCP server'senvblock. But Claude Code does not expand${CLAUDE_PROJECT_DIR}there —${VAR}expansion runs at config-parse time from Claude Code's own environment, whereCLAUDE_PROJECT_DIRis unset, so the token reaches the server literal. The loaders then failedreadFileSync, warned only to stderr (which the MCP stdio channel discards), and degraded to public-only. Completely silent;searchjust returned public packages.Verified against the live MCP server processes on this machine — every one carries the literal token, e.g.:
So facts and policy are affected too, not just corpus.
Fix
CLAUDE_PROJECT_DIRis injected into the spawned server'sprocess.env(the documented pattern), so the server can resolve the token itself. Newsrc/engine/overlay-path.ts→resolveOverlayPath():${VAR}/${VAR:-default}fromprocess.env(CLAUDE_PROJECT_DIRfalls back to cwd, never a bogus/.starlog/…)~CLAUDE_PROJECT_DIR || cwdloadPrivateCorpus,loadPrivateFacts, andloadPolicycall it at read time. This repairs already-wired installs with no re-init— theinitenv format is unchanged, so there's no migration. The misleadinginit.tscomment (claimed parse-time expansion) is corrected.doctor (#58)
checkPrivateOverlaysreportedPrivate overlays wired: [ok]on mere presence of the env keys — it never checked they resolved, so a stale absolute path or cross-project leak passed as healthy. It's the tool you'd run to catch #57, and it structurally couldn't. It now resolves each wired path exactly as the server will (resolveOverlayPath(value, projectDir)) and warns when it doesn't land on this project's.starlog/.Tests
TDD throughout (RED verified before each GREEN):
overlay-path.test.ts— 8 cases (token expansion, cwd fallback,~, relative,:-default, idempotence, explicit projectDir override)private-corpus.test.tsandfacts/service.test.tsdoctor.test.ts— misrouted/cross-project path now warnsFull suite green: 577/577, typecheck clean.
Notes for the reviewer
package.jsonversion bump — left for the release step perRELEASING.md. CHANGELOG entry staged under## 0.7.2; adjust the number if you prefer.search/factsmatch MCP behavior.🤖 Generated with Claude Code