fix(claude): honor CLAUDE_CONFIG_DIR for user-scope MCP config and the plugin cache#376
Merged
Merged
Conversation
Collaborator
|
@pjdoland can you resolve conflicts |
mbektas
approved these changes
Jun 16, 2026
The Claude CLI relocates its entire config dir, including session transcripts under projects/, when CLAUDE_CONFIG_DIR is set. The session pickers always read ~/.claude/projects, so on deployments that set the override (JupyterHub images commonly point it at a persistent volume) both pickers came up empty even though 'claude --resume' saw every session. Resolve the CLI's config dir through one shared util helper and make it the claude_home default for get_sessions_dir and list_all_sessions. The skills directory and spinner-verbs lookups already carried their own copies of the same expression; they now use the helper too, so the override semantics can't drift between surfaces. The helper deliberately does not expanduser the env value (the CLI doesn't) and treats an empty string as unset (matching the CLI's Node-style falsy check). The MCP user config (~/.claude.json) and the plugin cache have the same class of gap but need different relocation logic; tracked separately. Fixes plmbr#373
…e plugin cache The CLI relocates .claude.json to $CLAUDE_CONFIG_DIR/.claude.json when the override is set, but the MCP manager kept reading (and writing disabledMcpServers to) $HOME/.claude.json while CLI-mediated writes via 'claude mcp add' inherited the env and landed in the relocated file, so reads and writes diverged on override deployments. The plugin cache fallback similarly pointed at ~/.claude/plugins instead of the relocated cache. The .claude.json resolver is a private helper rather than a reuse of util.get_claude_config_dir because its default genuinely differs: the file lives in the home dir itself, not inside ~/.claude. The plugins fallback does route through the shared helper; the CLI gives CLAUDE_CODE_PLUGIN_CACHE_DIR precedence over the config dir and NBI matches that, verified against the CLI bundle and a sandboxed 'claude mcp add' run. Test hardening that the change made necessary: the unittest-style PATCH suite patches Path.home but pytest fixtures don't reach it, so a developer's exported CLAUDE_CONFIG_DIR would have sent the endpoint's write into their real relocated .claude.json; setUp now snapshots and scrubs the env. The Playwright webServer env drops the variable for the same reason, deleting it rather than blanking it because the CLI treats an empty string as a set (degenerate) config dir. Also dates 5.1.0 in the changelog (released 2026-06-08) and adds the missing compare links. Fixes plmbr#375
e4b5da8 to
07fadc4
Compare
Collaborator
Author
|
Resolved conflicts. |
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.
Summary
Follow-up to #374, finishing the
CLAUDE_CONFIG_DIRwork from #373. Two surfaces still hardcoded home-relative paths: the MCP management tab read user-scope servers (and wrotedisabledMcpServers) at~/.claude.jsoneven though the CLI relocates that file to$CLAUDE_CONFIG_DIR/.claude.jsonwhen the override is set, so file reads and CLI-mediated writes (claude mcp add, which inherits the env) diverged on override deployments. The Plugins panel's cache fallback pointed at~/.claude/pluginsinstead of the relocated cache.Stacked on #374; this branch contains that PR's commit until it merges, after which I'll rebase so only this change remains. Merge #374 first.
Solution
The load-bearing changes are two resolvers.
claude_mcp_manager.pygains a private_claude_user_config_path()rather than reusingutil.get_claude_config_dir, because the file's default genuinely differs: it lives in the home dir itself ($HOME/.claude.json), not inside~/.claude, while the override moves it inside the config dir.plugin_manager.py's_claude_plugins_root()fallback now routes through the shared helper;CLAUDE_CODE_PLUGIN_CACHE_DIRkeeps precedence over the config dir, which matches the CLI's own ordering.Both behaviors were verified against the installed CLI, not assumed: the CLI bundle's user-config getter is
join(process.env.CLAUDE_CONFIG_DIR || homedir(), ".claude.json")and its plugins root checks the cache-dir var beforeconfigDir/plugins; a sandboxedclaude mcp add -s userunder an override wrote to$dir/.claude.jsonand never touched the fake$HOME.The change also surfaced a real test hazard, fixed here: the unittest-style PATCH endpoint suite patches
Path.homebut pytest fixtures don't reachAsyncHTTPTestCase, so a developer withCLAUDE_CONFIG_DIRexported would have had the test suite write junk into their real relocated.claude.json.setUpnow snapshots and scrubs the env, and the PlaywrightwebServerenv drops the variable for the same seam (deleting it rather than blanking it, because the CLI treats an empty string as a set, degenerate config dir).Per your request this PR also dates 5.1.0 in the changelog (released 2026-06-08) and adds the missing
[5.1.0]compare link, retargeting[unreleased].Testing
TestClaudeUserConfigPath(reads the relocated config; override wins over a populated home config; default unchanged) andTestClaudePluginsRoot(falls back to$CLAUDE_CONFIG_DIR/plugins; explicit cache dir wins; home default unchanged with HOME/USERPROFILE pinned for POSIX and Windows).CLAUDE_CONFIG_DIRexported to a junk path, and the junk dir is never created by the touched tests (previously the PATCH test wrote real files there).tsc --noEmit(root and ui-tests),lint:check, jest 352.CLAUDE_CONFIG_DIRpointing at a synthetic.claude.json;GET /notebook-intelligence/claude-mcpreturned the user-scope server from the relocated file.Risks / follow-ups
src/components/claude-mcp-panel.tsxstill says~/.claude.jsonunconditionally; cosmetic copy, left for a small follow-up to keep this PR backend-scoped.~/.claude.jsonand~/.claude/plugins/mentions now carry the override notes, added in this PR once the code actually followed the override.Fixes #375