Phase 1 of the MCP server catalog: pure, GUI-free core functions plus the seed data/catalog.json (20 servers). No GUI wiring in this PR -- bcc.py is untouched; a follow-up PR adds the picker dialog. - load_catalog(): strict json.loads ONLY. The lenient repair pipeline (repair_json_text / parse_pasted_json*) is never used on catalog bytes, by design and by comment, so a signature always authenticates exactly what gets parsed. - validate_catalog(): rejects the whole file (not per-entry) on: bad schema/version types, missing tier-appropriate fields (basic needs config.command+args, link-only needs docs_url and no config), a command allowlist (npx/uvx/docker/node/python/python3 only), -e/--eval/-c denial for node/python, --privileged and root/$HOME volume-mount denial for docker, non-empty env_required values (hard rejection -- secrets never ship in the catalog), secret-looking args (reuses _TOKEN_PREFIXES/_is_secret_value rather than reimplementing), non-https URL fields, and non-ASCII code points in id/command/args (homoglyph defence). - verify_catalog_signature(): Ed25519 via the cryptography package, domain-separated message (the literal prefix "bcc-catalog-v1|" + raw bytes), accepts a match against any key in CATALOG_PUBKEYS (rotation-ready), never raises. - resolve_catalog(): picks the highest version among bundled/cached/remote candidates that EACH independently pass verify + validate -- the bundled catalog gets no implicit trust, closing the hole where an unsigned payload merged to main would win on being local. Anti-rollback (never regress below the best verified candidate already in hand) and anti-freeze (reject a jump of more than 1000 versions) built in. - catalog_entry_to_paste_json() / config_has_unfilled_placeholders(): small pure helpers the future GUI dialog will use to feed a catalog pick into the existing paste-import path and to gate Save on unfilled placeholder tokens. data/catalog.json: the provided 20-server seed, with a signed_at field added at the top level (lives inside the signed payload once real signing lands in #62). Wired into bcc.spec's PyInstaller datas so it bundles into the frozen app. Security requirements from the issue, and where they landed: - Catalog bytes never touch the lenient JSON repair path -- enforced by load_catalog()'s strict json.loads and a comment warning against wiring it in later. - env_required values are a hard rejection when non-empty, not a warning. - Secret-looking args are rejected at validation time, reusing the existing secret-detection helpers instead of duplicating them. - Non-ASCII id/command/args rejected (typosquat/homoglyph defence). - URL fields restricted to https://. - Ed25519 signature verification is domain-separated and never raises. - The bundled catalog is verified at runtime exactly like remote/cached -- no implicit trust for being local. - Anti-rollback and anti-freeze bounds on resolve_catalog's version comparison. Tests: 42 new tests added to tests/test_core.py (full suite: 239 passed, 1 pre-existing unrelated skip). ruff check and ruff format --check both clean.
Better Claude Config (BCC)
A small, cross-platform GUI for editing the mcpServers block of Claude Desktop
and Claude Code installs — without ever hand-writing JSON.
BCC only ever touches mcpServers (and its own _disabledMcpServers parking
key). Every other key in your config is preserved verbatim, in its original
order. Each write is atomic and makes a timestamped backup first.
Download (no Python required)
Pre-built self-contained binaries are attached to every GitHub Release:
| Platform | Download | Run |
|---|---|---|
| macOS | BetterClaudeConfig-macOS.zip |
Unzip → drag BetterClaudeConfig.app to Applications |
| Windows | BetterClaudeConfig-Windows.zip |
Unzip → double-click BetterClaudeConfig.exe |
| Linux | BetterClaudeConfig-Linux.tar.gz |
Extract → run BetterClaudeConfig |
macOS Gatekeeper note: the app is not notarized. On first launch, right-click → Open, or run
xattr -cr /Applications/BetterClaudeConfig.appin a terminal.
Run from source
pip install -r requirements.txt
python bcc.py
(Python 3.10+, PySide6 6.6+.)
What it does
-
Auto-discovers installs — scans the platform's app-support folder for any
Claude*directory (soClaudeandClaude-Workboth show up) and finds Claude Code's user-scope config at~/.claude.json(the fileclaude mcp addwrites). Use Add config… to point at any other file manually — e.g. a project's.mcp.json. -
Form-based editing — name, command, args (one per line), env vars, or for remote servers: URL, transport, and headers. No raw JSON.
-
Paste JSON — even broken JSON — drop in any snippet from an MCP doc (full
mcpServersblock, inner map, or a single bare server object); it's parsed and merged. The paste box parses as you type and auto-repairs the stuff docs and chat windows love to break: markdown fences, surrounding prose, comments, trailing or missing commas, smart quotes, single quotes, unquoted keys, and unclosed braces — and tells you exactly what it fixed before you commit. -
Drag & drop a
.jsonfile onto the window to import servers from it. -
Copy to ▸ — copy the selected server straight into your other install.
-
Active / Disabled sections — servers are shown in two labelled lists with live counts, so a server that's switched off in the config is obvious at a glance. Toggle a server's checkbox to move it between sections (disabled servers are parked under
_disabledMcpServers, which Claude ignores). -
Dependency check + diagnostics — each server shows whether its
commandis actually found on PATH:- ● green — found on the normal PATH; will work anywhere.
- ▲ amber (PATH-risk) — found, but only in a location BCC added on top of
the inherited PATH. It'll work when you launch Claude from a terminal, but a
double-clicked
.appmay not see it. This is the usual cause of "the tool says it's fine but Claude won't start the server." One click on Use full path ↳ rewrites the bare command (npx) to its absolute path so any launch finds it. - ● red (broken) — not found anywhere.
- ◆ blue — remote (url) server. Test connection does a live reachability check on a background thread (any HTTP response = reachable).
Each section header shows a broken / PATH-risk count badge, and selecting a flagged server auto-opens a Details panel with a full, copy-pasteable report: which command resolved to what, tailored install hints, and the exact PATH that was searched (with
+marking the dirs BCC added). Re-check re-scans PATH after you install something; Copy diagnostics grabs the whole report for a bug report.
After saving, restart that Claude install for changes to take effect.
Config locations it scans
Claude Desktop
| OS | Base it scans for Claude* |
|---|---|
| macOS | ~/Library/Application Support |
| Windows | %APPDATA% |
| Linux | ~/.config |
Claude Code (all platforms): ~/.claude.json (user scope). If servers are
found parked in ~/.claude/settings.json — where Claude Code ignores them — that
file is also listed, marked legacy, so you can copy them over.
Files
bcc.py— the GUI.bcc_core.py— all the file/JSON/validation/dependency logic (no GUI deps).test_core.py— unit suite for the core (python test_core.py).bcc.spec— PyInstaller build spec (cross-platform).scripts/build_icons.py— regeneratesicons/app.icnsandicons/app.icofrom source PNGs.
Building from source
pip install -r requirements-dev.txt
python scripts/build_icons.py # regenerate icons if needed
pyinstaller bcc.spec
# macOS → dist/BetterClaudeConfig.app
# Windows → dist/BetterClaudeConfig.exe
# Linux → dist/BetterClaudeConfig
Releases are built automatically by GitHub Actions (.github/workflows/release.yml) when a version tag is pushed:
git tag v1.0.0 && git push --tags
Rebrand
The accent color is one constant (ACCENT) at the top of bcc.py.
License
MIT — see LICENSE.