Files
better-claude-config/CLAUDE.md
T
the_ogandClaude Opus 4.8 4a95b370b9
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 24s
CI / Lint (ruff) (pull_request) Successful in 21s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 32s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 31s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 35s
CI / Catalog signature (pull_request) Successful in 26s
refactor: introduce ClientSpec adapter; route Claude Desktop + Code through it (#5)
Cross-client support (Cursor / Windsurf / VS Code) was blocked on Claude's
layout being hard-coded throughout the code: servers always under the literal
"mcpServers" key, a fixed set of Claude file locations, and "which client is
this?" answered by sniffing a filename. Adding a client that differs on any of
those axes meant chasing those assumptions through a dozen sites.

This is phase 1 of #5: the keystone refactor, with NO behaviour change. It adds
a ClientSpec adapter that captures the three things that vary across clients --
the top-level servers key, the config discovery paths, and the per-server value
shape -- plus the capability flags that were previously computed inline from a
filename (does the client expand ${VAR}? can we offer Restart?).

- ClientSpec (frozen dataclass): servers_key, disabled_key, config_filename,
  expands_env_refs, supports_restart, and entry_to_internal/entry_from_internal
  -- the per-server translation seam, identity for any mcpServers-shaped client,
  the single point a differently-shaped client (VS Code's type/inputs form)
  overrides.
- CLAUDE_DESKTOP and CLAUDE_CODE specs; both use mcpServers + the existing
  parking key, so their translation is the identity and nothing changes for
  today's users. resolve_client(path) reproduces the old filename rule exactly;
  each Profile now carries its resolved .client.
- extract_servers / apply_servers / _server_sections / external_change_summary
  take an optional spec and default to Claude's layout, so every existing call
  site and test that omits a spec is byte-for-byte unchanged. The cardinal rule
  now generalises: apply_servers only ever writes the client's own two keys,
  parameterised rather than hard-coded.
- profile_targets_claude_desktop and client_expands_env_refs are now thin reads
  off the profile's spec -- one source of truth for client identity instead of
  scattered filename checks -- with identical answers.
- GUI: the load, Copy-to, save and stale-merge paths pass the profile's spec
  into the core calls. Mechanical; no logic moved into bcc.py (which CI can't
  test -- no PySide6).

Tests: +14. Existing suite unchanged and green (behaviour preservation). A
synthetic non-mcpServers spec ("servers" key, a different disabled key, a
per-server `type` field) exercises the whole pipeline -- extract, apply,
masking, external-change diff -- proving the seam actually generalises before
any real client depends on it. 458 passed, 1 skipped; ruff clean.

Refs #5

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKwBecy6N83jnqQmw8ezwE
2026-08-04 02:53:51 +00:00

5.4 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

# Install runtime dependency
pip install -r requirements.txt
python bcc.py          # run the GUI

# Test & lint (no GUI / PySide6 needed — tests only exercise bcc_core)
pip install -r requirements-dev.txt
python -m pytest       # unit tests in tests/
ruff check .           # lint
ruff format .          # format (CI enforces ruff format --check)

# Build a self-contained binary
python scripts/build_icons.py   # regenerate icons/app.icns + icons/app.ico if needed
pyinstaller bcc.spec
# macOS  → dist/BetterClaudeConfig.app
# Windows → dist/BetterClaudeConfig.exe
# Linux  → dist/BetterClaudeConfig

Requires Python 3.10+. Runtime dependency: PySide6>=6.6. Tooling config (ruff, pytest, project metadata) lives in pyproject.toml. CI (.github/workflows/ci.yml) runs lint + tests on every push/PR; releases build on tag push (release.yml).

Architecture

The codebase is split into two layers:

bcc_core.py — All logic with no GUI imports. Contains:

  • Profile / ServerEntry dataclasses (the data model)

  • ClientSpec (issue #5, cross-client) — one adapter object per MCP host capturing everything client-specific: the top-level servers_key (Claude uses mcpServers; VS Code will use servers), the parking disabled_key, config config_filename, the capability flags (expands_env_refs, supports_restart), and a per-server entry_to_internal/entry_from_internal translation pair (identity for Claude; the seam a differently-shaped client overrides). CLAUDE_DESKTOP and CLAUDE_CODE are the two shipped specs; resolve_client(path) picks one by filename, and each Profile carries its resolved client. The read/write/diff functions take an optional spec and default to Claude's layout, so a call with no spec is unchanged.

  • discover_profiles() — scans the platform's app-support directory for Claude* folders (Claude Desktop) and always adds ~/.claude.json (Claude Code user scope — what claude mcp add writes). ~/.claude/settings.json is NOT a server config (it rejects mcpServers with a schema error) and is only surfaced, labelled legacy, if servers are found parked in it. Project-scope .mcp.json files can be opened via Add config…

  • load_config / extract_servers / apply_servers / write_config — the read/write pipeline; writes are atomic with rotating timestamped backups in .bcc_backups/

  • parse_pasted_json() / parse_pasted_json_verbose() — accepts three JSON shapes (full config, inner map, or bare server object). Input does not have to be valid JSON: repair_json_text() auto-fixes markdown fences, surrounding prose, // /* */ # comments, trailing/missing commas, smart quotes, single quotes, unquoted keys, Python/JS literals, and unclosed braces. The verbose variant also returns human-readable notes describing every repair applied (shown live in the paste dialog)

  • check_dependency() / diagnostics_text() / pin_command_path() — PATH resolution logic; distinguishes "found on normal PATH" (ok) vs "found only on augmented PATH" (warn) vs "not found" (missing). Uses an lru_cache-memoized augmented_path() that extends the inherited PATH with common runtime locations (nvm, homebrew, cargo, volta, etc.)

  • test_remote() — synchronous HTTP reachability check, intended to run off the UI thread

bcc.py — PySide6 GUI that is a thin shell over bcc_core. Key classes:

  • MainWindow — manages the profile combo, two server tables (active/disabled), action bar, and dirty state
  • ServerEditor — right-panel form with a QStackedWidget for stdio vs remote pages; calls core.check_dependency() on every field change
  • KeyValueTable — reusable widget for env vars and headers
  • ConnTester(QThread) — background thread for remote reachability tests

The cardinal rule: apply_servers() only ever writes the two keys the target client's servers live under — by default mcpServers and _disabledMcpServers, or whatever the profile's ClientSpec declares (servers_key + disabled_key). All other keys in the user's config are preserved verbatim and in their original order. The rule generalises across clients precisely because it is parameterised by the spec rather than hard-coded.

Disabled servers are parked under _disabledMcpServers (which Claude Desktop ignores) so they can be re-enabled without losing their definition.

Packaging

  • bcc.spec — PyInstaller spec; handles macOS (onedir → .app bundle) and Windows/Linux (onefile). Platform-aware icon selection.
  • icons/app.icns (macOS) and icons/app.ico (Windows) are pre-generated and committed; source PNGs are in icons/twin-gears/rounded/.
  • scripts/build_icons.py — regenerates both icon files from source PNGs. Uses iconutil (macOS-only) for .icns, Pillow for .ico.
  • .github/workflows/release.yml — builds on all three platforms on tag push, publishes as GitHub Release assets.

To release: git tag vX.Y.Z && git push --tags.

Key constants

  • ACCENT at the top of bcc.py — the one constant to change for a rebrand
  • KNOWN_FIELDS in bcc_core.py — fields the editor renders; anything else on a server object is preserved as _extra in ServerEditor
  • DISABLED_KEY = "_disabledMcpServers" — the parking key for disabled servers
  • MAX_BACKUPS = 15 — rolling backup limit per config file