Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfe05b4324 | ||
|
|
dffa0e152f |
@@ -3,16 +3,13 @@ name: CI
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths-ignore: ["**/*.md"]
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore: ["**/*.md"]
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Lint (ruff)
|
name: Lint (ruff)
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -34,11 +31,6 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
name: Tests (py${{ matrix.python }} / ${{ matrix.os }})
|
name: Tests (py${{ matrix.python }} / ${{ matrix.os }})
|
||||||
# Guards against a job that hangs mid-run (e.g. a wedged test). Note: this
|
|
||||||
# counts from when a runner PICKS UP the job, so it does not rescue a job
|
|
||||||
# stuck "Waiting to run" because the self-hosted Windows runner is offline —
|
|
||||||
# for that, bring the runner back or skip via paths-ignore (docs).
|
|
||||||
timeout-minutes: 15
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -93,7 +85,6 @@ jobs:
|
|||||||
catalog-signature:
|
catalog-signature:
|
||||||
name: Catalog signature
|
name: Catalog signature
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to **BetterClaudeConfig** are recorded here. The format is
|
|
||||||
based on [Keep a Changelog](https://keepachangelog.com/), and this project
|
|
||||||
follows [Semantic Versioning](https://semver.org/): breaking changes bump the
|
|
||||||
major, new features bump the minor, fixes bump the patch.
|
|
||||||
|
|
||||||
**When you open a PR, add a line under `[Unreleased]`.** At release time, that
|
|
||||||
section is renamed to the new version + date and a fresh `[Unreleased]` is
|
|
||||||
started.
|
|
||||||
|
|
||||||
## [Unreleased] — targeting 1.4.0
|
|
||||||
|
|
||||||
> ⚠️ **Using the `ssh-mcp` server?** Upstream `ssh-mcp` shipped a **breaking
|
|
||||||
> v2**: it removed the `--password`, `--sudoPassword`, `--suPassword` and
|
|
||||||
> `--disableSudo` command-line flags, and it now reads a `config.toml` sidecar
|
|
||||||
> file that overrides your command-line arguments. A config written for v1
|
|
||||||
> crashes on v2's startup. This release adds tools to detect and fix that.
|
|
||||||
> **BetterClaudeConfig itself has no breaking changes** — your existing configs
|
|
||||||
> keep working; nothing is changed without your click.
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **ssh-mcp v2 flag migration.** Detects the credential flags v2 removed and
|
|
||||||
one-click-moves each into the environment variable ssh-mcp now reads
|
|
||||||
(`--password` → `SSH_MCP_PASSWORD`; `--sudoPassword` / `--suPassword` →
|
|
||||||
`SSH_MCP_SUDO_PASSWORD`). Also flags `--maxChars=none`, whose meaning changed
|
|
||||||
between versions.
|
|
||||||
- **Sidecar-config awareness.** When a server reads a separate config file
|
|
||||||
(ssh-mcp's `config.toml`), BCC shows where that file actually lives on your
|
|
||||||
platform and warns when your command-line args are **inert** because the file
|
|
||||||
takes precedence — including when a file sits at the wrong, documented-but-
|
|
||||||
unused path.
|
|
||||||
- **In-app sidecar editor.** Edit that external config from inside BCC —
|
|
||||||
pick-lists for known fields, a raw-text fallback — written atomically with a
|
|
||||||
timestamped backup and locked to `0600`. No dropping to a terminal.
|
|
||||||
- **Live hot-reload.** External changes to a sidecar (or to your Claude config)
|
|
||||||
now surface without restarting BCC.
|
|
||||||
- **Version pinning for `npx` servers.** Spots servers launched with `-y` /
|
|
||||||
`@latest` that resolve a fresh version every run, shows the version currently
|
|
||||||
resolved, and offers a one-click **Pin to this version** — with a drift note
|
|
||||||
when a pin has fallen behind.
|
|
||||||
- **Permission pre-flight.** Warns when a credential-bearing config is readable
|
|
||||||
by other users on the machine and offers a one-click fix to `0600`/`0700`.
|
|
||||||
- **Light theme + system-following** (the dark theme is preserved exactly).
|
|
||||||
- **"Move to environment variable."** Convert a plaintext secret in a config
|
|
||||||
into a `${VAR}` reference in place — offered only on clients that actually
|
|
||||||
expand references, so it can't silently break a Claude Desktop config.
|
|
||||||
- **Cross-client foundation.** Claude Desktop and Claude Code now flow through a
|
|
||||||
single adapter — groundwork for supporting more clients.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- The update checker is now **visible** — a persistent banner plus a Help-menu
|
|
||||||
item — instead of being buried in the About dialog.
|
|
||||||
- Config writes share one atomic-write path; the temp file is created `0600`, so
|
|
||||||
a secret is never briefly world-readable mid-write.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Loading a config with a non-object server value no longer crashes, and named
|
|
||||||
server sets survive an external-change merge.
|
|
||||||
- Project profiles that share a directory basename are disambiguated, so you
|
|
||||||
can't accidentally edit the wrong `.mcp.json`.
|
|
||||||
|
|
||||||
### Internal / maintainer
|
|
||||||
- Signed-catalog core and a maintainer-only **Catalog Console** (review + sign),
|
|
||||||
with hardening of the review gate and a split of the signing keys. No
|
|
||||||
user-facing catalog browser ships yet.
|
|
||||||
|
|
||||||
## [1.3.0] — 2026-07-12
|
|
||||||
Named server sets, Claude Code project `.mcp.json` discovery, structural schema
|
|
||||||
lint, and UX polish (Ctrl+S to save, enable-all / disable-all).
|
|
||||||
|
|
||||||
## [1.2.1] — 2026-07-12
|
|
||||||
First shipped binaries: app-icon fix, a batch of audit fixes, and Windows
|
|
||||||
process-tree cleanup on spawn-tests.
|
|
||||||
|
|
||||||
## [1.2.0] — 2026-07-08
|
|
||||||
Server log viewer, duplicate-name conflict handling, a Restart-Claude button,
|
|
||||||
stale-file protection, an About dialog, and a notify-only update checker.
|
|
||||||
|
|
||||||
## [1.1.0] — 2026-07-04
|
|
||||||
Backup / restore UI and secret masking.
|
|
||||||
|
|
||||||
## [1.0.1] — 2026-06-29
|
|
||||||
## [1.0.0] — 2026-06-29
|
|
||||||
Initial releases: the core `mcpServers` editor with the lenient paste/repair
|
|
||||||
pipeline that tolerates malformed JSON.
|
|
||||||
|
|
||||||
<!-- Backfill for 1.0.0–1.3.0 is summarised from release notes; the Unreleased
|
|
||||||
section onward is maintained per-PR. -->
|
|
||||||
@@ -31,8 +31,6 @@ The codebase is split into two layers:
|
|||||||
|
|
||||||
**`bcc_core.py`** — All logic with no GUI imports. Contains:
|
**`bcc_core.py`** — All logic with no GUI imports. Contains:
|
||||||
- `Profile` / `ServerEntry` dataclasses (the data model)
|
- `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…
|
- `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/`
|
- `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)
|
- `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)
|
||||||
@@ -45,7 +43,7 @@ The codebase is split into two layers:
|
|||||||
- `KeyValueTable` — reusable widget for env vars and headers
|
- `KeyValueTable` — reusable widget for env vars and headers
|
||||||
- `ConnTester(QThread)` — background thread for remote reachability tests
|
- `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.
|
**The cardinal rule**: `apply_servers()` only ever writes to `mcpServers` and `_disabledMcpServers`. All other keys in the user's config are preserved verbatim and in their original order.
|
||||||
|
|
||||||
Disabled servers are parked under `_disabledMcpServers` (which Claude Desktop ignores) so they can be re-enabled without losing their definition.
|
Disabled servers are parked under `_disabledMcpServers` (which Claude Desktop ignores) so they can be re-enabled without losing their definition.
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
# PR #87 — "Move to environment variable" (#83): manual test checklist
|
|
||||||
|
|
||||||
The logic is covered by 15 unit tests in CI; what CI **can't** exercise is the GUI (no PySide6). This checklist is only the parts a human needs to click. Should take ~10 minutes.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/Documents/Claude/Projects/BetterClaudeConfig/better-claude-config
|
|
||||||
git fetch origin
|
|
||||||
git checkout feat/83-move-to-env-var
|
|
||||||
git pull # ensure you're on 8fdbe90 or later
|
|
||||||
source .venv/bin/activate # or recreate: python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt
|
|
||||||
python bcc.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Pick a **Claude Code** profile (e.g. `~/.claude.json`) that has, or add, a server with an env value that looks like a secret — e.g. `env: { "API_KEY": "ghp_test123" }`. (You can use a throwaway value; nothing is sent anywhere.)
|
|
||||||
|
|
||||||
## The checklist
|
|
||||||
|
|
||||||
### Gating — where the action appears
|
|
||||||
- [ ] Right-click the **value cell** of a secret env row (`API_KEY`) on a **Claude Code** profile → a **"Move to environment variable…"** item appears.
|
|
||||||
- [ ] Right-click a **non-secret** row (e.g. `REGION` = `us-east-1`) → the item does **not** appear.
|
|
||||||
- [ ] Right-click a row whose value is already a reference (`${API_KEY}`) → the item does **not** appear.
|
|
||||||
- [ ] Switch to a **Claude Desktop** profile (a `claude_desktop_config.json`), right-click the same kind of secret row → the item does **not** appear. (Desktop doesn't expand `${VAR}`, so offering it would break the config — this is the important gate.)
|
|
||||||
|
|
||||||
### The dialog
|
|
||||||
- [ ] Trigger the action → dialog opens with **Variable** pre-filled from the key, sanitized to a legal shell name (e.g. `api-key` → `API_KEY`).
|
|
||||||
- [ ] Edit the variable name → the shown **shell line updates live** and matches your platform (`export VAR='…'` on macOS/Linux, `setx VAR "…"` on Windows), with the other platform shown in parentheses.
|
|
||||||
- [ ] If you type a variable name that **is already set** in your shell environment, the green "already looks set" note appears; if not, it's hidden.
|
|
||||||
- [ ] **Cancel** → nothing changes (value still the raw secret, no dirty state).
|
|
||||||
|
|
||||||
### The conversion
|
|
||||||
- [ ] **Move && copy secret** → the cell now shows the reference `${VAR}` (visible, **not** masked to dots), and the window goes dirty (Save enabled).
|
|
||||||
- [ ] Paste from your clipboard somewhere → it's the **original secret value** (handed back before removal).
|
|
||||||
- [ ] The reference value is **not** flagged as a secret warning anymore (it's the recommended state).
|
|
||||||
|
|
||||||
### Headers + persistence
|
|
||||||
- [ ] Repeat on a **remote server's Headers** table (e.g. an `Authorization` header) → same behavior.
|
|
||||||
- [ ] **Save**, then open the config file on disk in a text editor → the servers block holds `${VAR}`, and the **plaintext secret is gone** from the file.
|
|
||||||
- [ ] Re-open the profile in BCC → the row still shows `${VAR}` (round-trips).
|
|
||||||
|
|
||||||
### Undo (nice-to-have)
|
|
||||||
- [ ] After a conversion, **Ctrl+Z / Cmd+Z** restores the previous value.
|
|
||||||
|
|
||||||
## Known scope (not bugs)
|
|
||||||
- **Args rows** are out of scope for this PR — the core supports them, but the args editor is a free-text widget, so wiring that UI is a deliberate follow-up. Right-clicking args won't offer the action yet.
|
|
||||||
- The "already set" check reads **BCC's** environment, which may differ from the client's — it's advisory, worded that way.
|
|
||||||
|
|
||||||
## If anything's off
|
|
||||||
Tell me which checkbox failed and what you saw; I'll fix on the branch and re-push. If everything passes, approve/merge #87 (or tell me to merge it).
|
|
||||||
@@ -31,7 +31,11 @@ a = Analysis(
|
|||||||
["bcc.py"],
|
["bcc.py"],
|
||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[("icons", "icons"), ("data/catalog.json", "data")],
|
datas=[
|
||||||
|
("icons", "icons"),
|
||||||
|
("data/catalog.json", "data"),
|
||||||
|
("data/catalog.json.sig", "data"),
|
||||||
|
],
|
||||||
hiddenimports=[],
|
hiddenimports=[],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
hooksconfig={},
|
hooksconfig={},
|
||||||
|
|||||||
+254
-2317
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
# Runtime (also in requirements.txt)
|
# Runtime (also in requirements.txt)
|
||||||
PySide6>=6.6
|
PySide6>=6.6
|
||||||
cryptography>=42.0 # catalog signature verification (bcc_core) + release checksum signing
|
|
||||||
|
|
||||||
# Build / packaging
|
# Build / packaging
|
||||||
pyinstaller>=6.0
|
pyinstaller>=6.0
|
||||||
@@ -9,3 +8,4 @@ pillow>=10.0 # generates icons/app.ico during CI (Windows build)
|
|||||||
# Test / lint
|
# Test / lint
|
||||||
pytest>=8.0
|
pytest>=8.0
|
||||||
ruff>=0.6
|
ruff>=0.6
|
||||||
|
cryptography>=42.0 # release checksum signing (scripts/sign_checksums.py)
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
PySide6>=6.6
|
PySide6>=6.6
|
||||||
cryptography>=42.0 # bcc_core imports it at load (catalog signature verification)
|
|
||||||
|
|||||||
+318
-1690
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user