When Claude Desktop is installed as an MSIX/Store package, Windows redirects filesystem writes to a per-package virtualized location (%LOCALAPPDATA%\Packages\<PackageFamilyName>\LocalCache\Roaming\Claude\claude_desktop_config.json) instead of the normal %APPDATA%\Claude\claude_desktop_config.json. Edits BCC (or the user) writes to the plain path can be silently ignored by the running app. See anthropics/claude-code issues #26073, #29100, #38830.
Changes
bcc_core.py (pure, unit-tested, no Qt):
msix_config_paths(localappdata=None) -> list[Path] — globs Packages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.json under the given (or env-derived) LOCALAPPDATA base and returns every match that actually exists on disk. Platform-independent by design so it's directly testable via tmp_path.
detect_msix_claude(appdata=None, localappdata=None) -> Path | None — the Windows-gated entry point (sys.platform.startswith("win")); safe no-op on macOS/Linux. Returns the first virtualized config found, or None.
msix_warning_text(appdata=None, localappdata=None) -> str | None — a one-line, paste-safe warning (paths only, no secrets/env values) for when a virtualized config was detected and it actually differs from the plain %APPDATA%\Claude\ path.
discover_profiles() now also calls detect_msix_claude() and, when it finds a virtualized install not already covered by the normal Claude* scan, adds it as its own profile labeled Claude (Microsoft Store / MSIX) — so users can select and edit the file the app actually reads directly from the dropdown, not just be warned about it.
bcc.py (GUI, minimal/defensive wiring):
MainWindow._maybe_warn_msix(), called at the end of reload_profiles(), wraps core.msix_warning_text() in a try/except (must never block startup) and, when non-None, appends the warning to the status bar text + sets it as the tooltip. On non-Windows this is a guaranteed no-op via the sys.platform gate in core, so nothing changes for macOS/Linux users.
Tests
Added 15 new tests in tests/test_core.py covering:
msix_config_paths: finds a package config, ignores non-Claude packages, empty when package dir has no config yet, empty when no Packages dir, reads LOCALAPPDATA env by default.
detect_msix_claude: None on non-Windows (even with a real match on disk), finds the config on "Windows" (sys.platform monkeypatched to win32), None when nothing present.
msix_warning_text: None on non-Windows, None when nothing detected, warns with both paths present in the message when they differ, None when the plain and virtualized paths happen to coincide.
discover_profiles: adds the MSIX profile on "Windows" when detected, omits it when nothing's detected, omits it entirely on non-Windows.
Test count: 108 → 123 in tests/test_core.py (136 → 151 total incl. test_json_repair.py). All passing.
Verification
ruff check . — clean
ruff format --check . — clean
PYTHONPATH=. python -m pytest -q — all pass
bcc.py validated via ruff + ast.parse only (PySide6 not installed in the sandbox; GUI change is a small, defensive, try/except-wrapped status-bar append with no new imports).
Caveats — needs real-Windows verification
The detection glob pattern (Packages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.json) is based on the publicly known MSIX virtualization layout; the actual Anthropic package family name/folder naming should be confirmed against a real Store install.
GUI wiring (_maybe_warn_msix, the new profile entry showing up in the dropdown) has not been run against real PySide6/Qt or a real Windows+MSIX machine — only statically validated (ruff, AST parse, code review against existing GUI patterns in bcc.py).
No attempt was made to auto-migrate or offer to copy servers from the plain path into the virtualized one — this PR only detects and surfaces, per the issue's scope.
Closes #7
## Problem
When Claude Desktop is installed as an MSIX/Store package, Windows redirects filesystem writes to a per-package virtualized location (`%LOCALAPPDATA%\Packages\<PackageFamilyName>\LocalCache\Roaming\Claude\claude_desktop_config.json`) instead of the normal `%APPDATA%\Claude\claude_desktop_config.json`. Edits BCC (or the user) writes to the plain path can be silently ignored by the running app. See anthropics/claude-code issues #26073, #29100, #38830.
## Changes
**`bcc_core.py`** (pure, unit-tested, no Qt):
- `msix_config_paths(localappdata=None) -> list[Path]` — globs `Packages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.json` under the given (or env-derived) `LOCALAPPDATA` base and returns every match that actually exists on disk. Platform-independent by design so it's directly testable via `tmp_path`.
- `detect_msix_claude(appdata=None, localappdata=None) -> Path | None` — the Windows-gated entry point (`sys.platform.startswith("win")`); safe no-op on macOS/Linux. Returns the first virtualized config found, or `None`.
- `msix_warning_text(appdata=None, localappdata=None) -> str | None` — a one-line, paste-safe warning (paths only, no secrets/env values) for when a virtualized config was detected *and* it actually differs from the plain `%APPDATA%\Claude\` path.
- `discover_profiles()` now also calls `detect_msix_claude()` and, when it finds a virtualized install not already covered by the normal `Claude*` scan, adds it as its own profile labeled `Claude (Microsoft Store / MSIX)` — so users can select and edit the file the app actually reads directly from the dropdown, not just be warned about it.
**`bcc.py`** (GUI, minimal/defensive wiring):
- `MainWindow._maybe_warn_msix()`, called at the end of `reload_profiles()`, wraps `core.msix_warning_text()` in a `try/except` (must never block startup) and, when non-`None`, appends the warning to the status bar text + sets it as the tooltip. On non-Windows this is a guaranteed no-op via the `sys.platform` gate in core, so nothing changes for macOS/Linux users.
## Tests
Added 15 new tests in `tests/test_core.py` covering:
- `msix_config_paths`: finds a package config, ignores non-Claude packages, empty when package dir has no config yet, empty when no `Packages` dir, reads `LOCALAPPDATA` env by default.
- `detect_msix_claude`: `None` on non-Windows (even with a real match on disk), finds the config on "Windows" (`sys.platform` monkeypatched to `win32`), `None` when nothing present.
- `msix_warning_text`: `None` on non-Windows, `None` when nothing detected, warns with both paths present in the message when they differ, `None` when the plain and virtualized paths happen to coincide.
- `discover_profiles`: adds the MSIX profile on "Windows" when detected, omits it when nothing's detected, omits it entirely on non-Windows.
Test count: 108 → 123 in `tests/test_core.py` (136 → 151 total incl. `test_json_repair.py`). All passing.
## Verification
- `ruff check .` — clean
- `ruff format --check .` — clean
- `PYTHONPATH=. python -m pytest -q` — all pass
- `bcc.py` validated via `ruff` + `ast.parse` only (PySide6 not installed in the sandbox; GUI change is a small, defensive, try/except-wrapped status-bar append with no new imports).
## Caveats — needs real-Windows verification
- The detection glob pattern (`Packages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.json`) is based on the publicly known MSIX virtualization layout; the actual Anthropic package family name/folder naming should be confirmed against a real Store install.
- GUI wiring (`_maybe_warn_msix`, the new profile entry showing up in the dropdown) has not been run against real PySide6/Qt or a real Windows+MSIX machine — only statically validated (ruff, AST parse, code review against existing GUI patterns in `bcc.py`).
- No attempt was made to auto-migrate or offer to copy servers from the plain path into the virtualized one — this PR only detects and surfaces, per the issue's scope.
the_og
added the P1 label 2026-07-08 11:51:44 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #7
Problem
When Claude Desktop is installed as an MSIX/Store package, Windows redirects filesystem writes to a per-package virtualized location (
%LOCALAPPDATA%\Packages\<PackageFamilyName>\LocalCache\Roaming\Claude\claude_desktop_config.json) instead of the normal%APPDATA%\Claude\claude_desktop_config.json. Edits BCC (or the user) writes to the plain path can be silently ignored by the running app. See anthropics/claude-code issues #26073, #29100, #38830.Changes
bcc_core.py(pure, unit-tested, no Qt):msix_config_paths(localappdata=None) -> list[Path]— globsPackages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.jsonunder the given (or env-derived)LOCALAPPDATAbase and returns every match that actually exists on disk. Platform-independent by design so it's directly testable viatmp_path.detect_msix_claude(appdata=None, localappdata=None) -> Path | None— the Windows-gated entry point (sys.platform.startswith("win")); safe no-op on macOS/Linux. Returns the first virtualized config found, orNone.msix_warning_text(appdata=None, localappdata=None) -> str | None— a one-line, paste-safe warning (paths only, no secrets/env values) for when a virtualized config was detected and it actually differs from the plain%APPDATA%\Claude\path.discover_profiles()now also callsdetect_msix_claude()and, when it finds a virtualized install not already covered by the normalClaude*scan, adds it as its own profile labeledClaude (Microsoft Store / MSIX)— so users can select and edit the file the app actually reads directly from the dropdown, not just be warned about it.bcc.py(GUI, minimal/defensive wiring):MainWindow._maybe_warn_msix(), called at the end ofreload_profiles(), wrapscore.msix_warning_text()in atry/except(must never block startup) and, when non-None, appends the warning to the status bar text + sets it as the tooltip. On non-Windows this is a guaranteed no-op via thesys.platformgate in core, so nothing changes for macOS/Linux users.Tests
Added 15 new tests in
tests/test_core.pycovering:msix_config_paths: finds a package config, ignores non-Claude packages, empty when package dir has no config yet, empty when noPackagesdir, readsLOCALAPPDATAenv by default.detect_msix_claude:Noneon non-Windows (even with a real match on disk), finds the config on "Windows" (sys.platformmonkeypatched towin32),Nonewhen nothing present.msix_warning_text:Noneon non-Windows,Nonewhen nothing detected, warns with both paths present in the message when they differ,Nonewhen the plain and virtualized paths happen to coincide.discover_profiles: adds the MSIX profile on "Windows" when detected, omits it when nothing's detected, omits it entirely on non-Windows.Test count: 108 → 123 in
tests/test_core.py(136 → 151 total incl.test_json_repair.py). All passing.Verification
ruff check .— cleanruff format --check .— cleanPYTHONPATH=. python -m pytest -q— all passbcc.pyvalidated viaruff+ast.parseonly (PySide6 not installed in the sandbox; GUI change is a small, defensive, try/except-wrapped status-bar append with no new imports).Caveats — needs real-Windows verification
Packages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.json) is based on the publicly known MSIX virtualization layout; the actual Anthropic package family name/folder naming should be confirmed against a real Store install._maybe_warn_msix, the new profile entry showing up in the dropdown) has not been run against real PySide6/Qt or a real Windows+MSIX machine — only statically validated (ruff, AST parse, code review against existing GUI patterns inbcc.py).