Tracking issue for the 18 field notes from the 2026-08-02 ssh-mcp setup (BCC-ROADMAP-ssh-mcp-notes-COWORK.md). Through-line: BCC models which host reads a config (ClientSpec) but not which server package it's editing. The seed we shipped (FLAG_ENV_MIGRATIONS, #89) grew into a ServerSpec axis; the rest falls out additively.
Non-reversing rule: sidecar (TOML) editing is a new write target — its own writer reuses write_config's atomic-rename + _make_backup; it does not go through apply_servers. ClientSpec untouched; ServerSpec orthogonal.
Stacked — #91/#92 are siblings off #90; #93 hangs off #91 (needs its sidecar path resolution). Each PR's base is its parent branch so per-PR diffs are clean. Merge order: #90 → #91 → #92 → #93 (merge #90 first; the rest retarget to main cleanly). Prefer merge-commit/rebase, not squash (squashing a parent breaks a child's base).
Suite: 509 → 529 passing (1 skipped). ruff check + format --check clean on all four. Touched only bcc_core.py / bcc.py / tests/test_core.py. No catalog/signing/CI-workflow edits. No network calls; no sidecar-content writer added (cardinal rule intact — apply_servers still writes only mcpServers/_disabledMcpServers).
Decisions / caveats to review
#90 intentionally updated one #89 test.test_removed_flag_warnings_covers_migratable_and_manual encoded the old warn-only --sudoPassword. Verified facts + issue #90 mandate adding sudo/su → SSH_MCP_SUDO_PASSWORD to the auto-migrate map, so it now moves into env. The migration logic is byte-for-byte unchanged — only the registry data grew. All other #89 tests untouched.
#91count_toml_profiles is a documented heuristic (no tomllib on py3.10; exact ssh-mcp profile schema unverifiable here). It only advises, never edits/blocks — worth confirming against the real package schema.
Cross-platform tests: the CI Windows runner renders str(Path("/Users/…")) with backslashes; path assertions use .as_posix(). #91 and #93 each needed one follow-up commit for this — caught before declaring green.
GUI needs a human click-through (headless smoke confirms wiring, not live firing — no ssh-mcp config.toml on the CI box)
#91 sidecar advisory (args-inert + wrong-path) with a real config.toml present.
#92 version badge + "Pin to <v>" button on an npx -y ssh-mcp server (resolve → pin → badge flips to pinned; drift note when the cache moved past a pin).
#93 permission warning + "Fix permissions" button against a config.toml at 0644 (POSIX only).
New rows hide for remote servers / no selection.
#90 has no GUI (pure core). Full write-up in OVERNIGHT-REPORT-COWORK.md at the repo root.
## Overnight cut — #90–#93 all implemented, PR'd, CI-green ✅
Worked the four filed issues unattended, one PR each off fresh `origin/main`. **Nothing merged, tagged, or released.** Every PR stays open for review.
| PR | Branch | Issue | Base | CI |
|----|--------|-------|------|----|
| #96 | `feat/90` | #90 ServerSpec spine (P1) | `main` | ✅ green |
| #97 | `feat/91` | #91 Sidecar detection + precedence (P0) | `feat/90` | ✅ green |
| #98 | `feat/92` | #92 Version pin/drift (P0) | `feat/90` | ✅ green |
| #99 | `feat/93` | #93 Permission pre-flight (P1) | `feat/91` | ✅ green |
**Stacked** — #91/#92 are siblings off #90; #93 hangs off #91 (needs its sidecar path resolution). Each PR's base is its parent branch so per-PR diffs are clean. **Merge order: #90 → #91 → #92 → #93** (merge #90 first; the rest retarget to `main` cleanly). Prefer merge-commit/rebase, **not squash** (squashing a parent breaks a child's base).
Suite: **509 → 529** passing (1 skipped). `ruff check` + `format --check` clean on all four. Touched only `bcc_core.py` / `bcc.py` / `tests/test_core.py`. No catalog/signing/CI-workflow edits. No network calls; no sidecar-content writer added (cardinal rule intact — `apply_servers` still writes only `mcpServers`/`_disabledMcpServers`).
### Decisions / caveats to review
- **#90 intentionally updated one #89 test.** `test_removed_flag_warnings_covers_migratable_and_manual` encoded the old warn-only `--sudoPassword`. Verified facts + issue #90 mandate adding sudo/su → `SSH_MCP_SUDO_PASSWORD` to the auto-migrate map, so it now moves into env. **The migration logic is byte-for-byte unchanged — only the registry data grew.** All other #89 tests untouched.
- **#91 `count_toml_profiles` is a documented heuristic** (no `tomllib` on py3.10; exact ssh-mcp profile schema unverifiable here). It only advises, never edits/blocks — worth confirming against the real package schema.
- **Cross-platform tests:** the CI Windows runner renders `str(Path("/Users/…"))` with backslashes; path assertions use `.as_posix()`. #91 and #93 each needed one follow-up commit for this — caught before declaring green.
### GUI needs a human click-through (headless smoke confirms wiring, not live firing — no ssh-mcp `config.toml` on the CI box)
1. **#91** sidecar advisory (args-inert + wrong-path) with a real `config.toml` present.
2. **#92** version badge + "Pin to `<v>`" button on an `npx -y ssh-mcp` server (resolve → pin → badge flips to pinned; drift note when the cache moved past a pin).
3. **#93** permission warning + "Fix permissions" button against a `config.toml` at 0644 (POSIX only).
4. New rows hide for remote servers / no selection.
#90 has no GUI (pure core). Full write-up in `OVERNIGHT-REPORT-COWORK.md` at the repo root.
Branches are stacked (not siblings), per the mandate: feat/102 is branched off feat/101 and its PR base is set to feat/101, so #104's diff is only the editor. Merge #101 first; then #104's base auto-retargets to main (or rebase). This avoids last round's silent sibling-merge conflict.
Dependency decision — no new runtime dependency (this is the load-bearing call)
The task floated tomli+tomli-w or a hand-roll using tomllib/tomli for reads. Neither survives the real CI constraint, which I discovered by reading .github/workflows/ci.yml (off-limits to edit):
The test job runs pip install pytest cryptography — notrequirements.txt.
The catalog-signature job does import bcc_core with only cryptography installed.
Therefore: (a) bcc_core must import with only cryptography present → no top-level TOML import is possible; and (b) any pip TOML dep would leave the new core untested or red on CI, since CI never installs it and I can't change the workflow. Even the task's option (b) fails: the tomllib read path is untested on the 3.10 runner (no stdlib tomllib).
Chosen: stdlib-only, uniform across 3.10/3.12/3.13 — a lenient scalar reader + a surgical line-editing writer that rewrites only the one key it's asked to. Comments, formatting, ordering and unknown keys/tables round-trip untouched — strictly safer than parse→dict→reserialize, and tomli-w wouldn't comment-preserve anyway. Runtime deps stay exactly PySide6 + cryptography. requirements.txt unchanged.
Core (unit-tested):sidecar_watch_paths (file + its dir + doc path, de-duped), sidecar_state_fingerprint (hashable snapshot folding #91 sidecar status + #93 permission status), sidecar_state_changed.
GUI (thin wiring):QFileSystemWatcher over the selection's sidecar + BCC's own config, debounced 300 ms; re-arm on every event (atomic-rename replaces drop the inode); focus-in fallback via changeEvent(ActivationChange); recheck_advisories() recomputes only warning labels (never field values, so it can't clobber unsaved edits); an external edit to BCC's own config shows a non-destructive Reload banner (confirm-on-dirty), never a silent overwrite.
Core (unit-tested):read_toml_section, toml_sections, set_toml_value/update_toml (surgical, CRLF-safe, escaping, create-section, delete-on-None), validate_sidecar_values (enum/port on managed fields only; unknown keys pass through), write_sidecar (atomic + rotating backup + chmod 0600/0700 via #93; neverapply_servers). Refactor: _make_backup/write_config now share _atomic_write_text; _make_backup keys on the file suffix (JSON naming byte-identical, so a .toml sidecar and .json config keep separate backup pools).
GUI (thin wiring): "Edit config…" button beside the sidecar advisory → SidecarEditorDialog (section picker, enum pick-lists, range-bounded port spinner, raw-TOML fallback). Save applies only changed managed fields on top of the raw text, validates, writes, then re-runs advisories through #101's path so they update live.
GUI elements needing a human click-through
CI has no PySide6, so none of the below is exercised by CI — I smoke-tested each headlessly (QT_QPA_PLATFORM=offscreen: dialog build, section detection, changed-field diff, surgical save with comment preserved + 0600 applied, and Edit-button visibility). Please verify interactively:
Add an ssh-mcp server (npx -y ssh-mcp --host=h --user=u), select it.
In a terminal create the sidecar (macOS): mkdir -p ~/Library/Application\ Support/ssh-mcp && printf '[server]\nhost="h"\n' > ~/Library/Application\ Support/ssh-mcp/config.toml. Within ~1 s the "arguments are inert" advisory appears — no restart.
chmod 644 that file → the permission advisory appears; chmod 600 → it clears.
Edit BCC's own config JSON in another editor and save → the Reload banner appears; click Reload from disk (prompts if you have unsaved edits — confirm it does not clobber them).
Delete the config.toml → advisory clears. Alt-tab away and back to confirm the focus-in fallback also refreshes.
With the ssh-mcp server selected, an "Edit config…" button shows beside the advisory (also shows before the file exists, so you can create it).
Click it. With no file yet, the raw editor is empty and the section combo shows (top level). Pick a [server] section (type [server] in raw or use the combo once a section exists), set role = admin via the pick-list, Save.
Confirm: the dialog reports the backup name + 0600; ls -l shows -rw-------; the "args inert" advisory now shows (that's #101 firing).
Re-open, change port (spinner) and auth (pick-list), add a # comment in the raw text, Save. Re-open → the comment survived and the values changed (surgical round-trip).
Confirm invalid enum values are structurally impossible via the pick-lists.
Known limitations (by design; none block — noted for click-through)
Section-switch rebaselines the form. Changing the section combo re-reads the pick-lists from raw text for the new section and resets the "changed since load" baseline. So pick-list edits made in section A, then abandoned by switching to section B before Save, are silently dropped. Fine for a first pass; the raw editor is always the source of truth.
Raw-text edits bypass schema validation, intentionally. Save validates only the form-changed managed fields, not raw-editor content — typing auth = "sshkey" directly in raw and saving is not rejected. This is the deliberate reconciliation of "reject out-of-enum" vs "preserve unknown keys": whole-file validation would block saves on a pre-existing invalid/unknown key the user never touched. The pick-list guarantee is form-only; the raw editor is the power-user escape hatch.
First-pass editor scope. One section targeted at a time; multi-profile editing is via the section picker + raw text. No comment-authoring UI (comments are preserved, not created). These are additive follow-ups if wanted.
Nothing skipped / blocked
Both issues landed CI-green. No irreversible actions taken: no merges, no force-push, no tags, no pushes to main, no touches outside bcc_core.py / bcc.py / tests/ (requirements.txt untouched — zero new deps). apply_servers unchanged; ClientSpec/ServerSpec unchanged (added on top).
Cardinal-rule check
apply_servers still only writes mcpServers/_disabledMcpServers. The sidecar editor writes a different file through its ownwrite_sidecar, which reuseswrite_config's atomic-rename + _make_backup but never routes through apply_servers (there's an explicit test asserting no server keys leak into a sidecar write).
**Overnight update — #101 and #102 landed (stacked, both CI-green, not merged).**
# Overnight report 2 — hot-reload (#101) + in-app sidecar editor (#102)
Two stacked PRs, both CI-green, non-destructive. **Nothing merged.**
## PRs (recommended review/merge order: #101 → #102)
| PR | Issue | Branch | Base | CI |
|----|-------|--------|------|----|
| [#103](https://git.avezzano.io/the_og/better-claude-config/pulls/103) | #101 — live hot-reload | `feat/101` | `main` | ✅ green |
| [#104](https://git.avezzano.io/the_og/better-claude-config/pulls/104) | #102 — in-app sidecar editor | `feat/102` | **`feat/101`** | ✅ green |
Branches are **stacked** (not siblings), per the mandate: `feat/102` is branched off `feat/101` and its PR base is set to `feat/101`, so #104's diff is only the editor. Merge #101 first; then #104's base auto-retargets to `main` (or rebase). This avoids last round's silent sibling-merge conflict.
## Dependency decision — **no new runtime dependency** (this is the load-bearing call)
The task floated `tomli`+`tomli-w` or a hand-roll using `tomllib`/`tomli` for reads. **Neither survives the real CI constraint**, which I discovered by reading `.github/workflows/ci.yml` (off-limits to edit):
- The **test job** runs `pip install pytest cryptography` — **not** `requirements.txt`.
- The **catalog-signature job** does `import bcc_core` with only `cryptography` installed.
Therefore: (a) `bcc_core` must import with only `cryptography` present → **no top-level TOML import is possible**; and (b) any pip TOML dep would leave the new core **untested or red** on CI, since CI never installs it and I can't change the workflow. Even the task's option (b) fails: the `tomllib` read path is untested on the **3.10** runner (no stdlib `tomllib`).
**Chosen:** stdlib-only, uniform across 3.10/3.12/3.13 — a lenient scalar reader + a **surgical line-editing writer** that rewrites only the one key it's asked to. Comments, formatting, ordering and unknown keys/tables round-trip untouched — strictly safer than parse→dict→reserialize, and `tomli-w` wouldn't comment-preserve anyway. Runtime deps stay exactly `PySide6` + `cryptography`. `requirements.txt` unchanged.
## What shipped
### #101 — hot-reload (PR #103)
- **Core (unit-tested):** `sidecar_watch_paths` (file + its dir + doc path, de-duped), `sidecar_state_fingerprint` (hashable snapshot folding #91 sidecar status + #93 permission status), `sidecar_state_changed`.
- **GUI (thin wiring):** `QFileSystemWatcher` over the selection's sidecar + BCC's own config, **debounced 300 ms**; **re-arm on every event** (atomic-rename replaces drop the inode); **focus-in fallback** via `changeEvent(ActivationChange)`; `recheck_advisories()` recomputes only warning labels (never field values, so it can't clobber unsaved edits); an external edit to BCC's own config shows a **non-destructive Reload banner** (confirm-on-dirty), never a silent overwrite.
### #102 — in-app sidecar editor (PR #104)
- **Core (unit-tested):** `read_toml_section`, `toml_sections`, `set_toml_value`/`update_toml` (surgical, CRLF-safe, escaping, create-section, delete-on-None), `validate_sidecar_values` (enum/port on **managed** fields only; unknown keys pass through), `write_sidecar` (atomic + rotating backup + chmod 0600/0700 via #93; **never** `apply_servers`). Refactor: `_make_backup`/`write_config` now share `_atomic_write_text`; `_make_backup` keys on the file suffix (JSON naming byte-identical, so a `.toml` sidecar and `.json` config keep separate backup pools).
- **GUI (thin wiring):** "Edit config…" button beside the sidecar advisory → `SidecarEditorDialog` (section picker, enum **pick-lists**, range-bounded port **spinner**, raw-TOML fallback). Save applies only changed managed fields on top of the raw text, validates, writes, then re-runs advisories through #101's path so they update live.
## GUI elements needing a human click-through
CI has no PySide6, so none of the below is exercised by CI — I smoke-tested each headlessly (`QT_QPA_PLATFORM=offscreen`: dialog build, section detection, changed-field diff, surgical save with comment preserved + 0600 applied, and Edit-button visibility). Please verify interactively:
**#101 — hot-reload**
1. Add an ssh-mcp server (`npx -y ssh-mcp --host=h --user=u`), select it.
2. In a terminal create the sidecar (macOS): `mkdir -p ~/Library/Application\ Support/ssh-mcp && printf '[server]\nhost="h"\n' > ~/Library/Application\ Support/ssh-mcp/config.toml`. Within ~1 s the **"arguments are inert"** advisory appears — no restart.
3. `chmod 644` that file → the **permission** advisory appears; `chmod 600` → it clears.
4. Edit BCC's own config JSON in another editor and save → the **Reload banner** appears; click **Reload from disk** (prompts if you have unsaved edits — confirm it does not clobber them).
5. Delete the `config.toml` → advisory clears. Alt-tab away and back to confirm the **focus-in fallback** also refreshes.
**#102 — sidecar editor**
1. With the ssh-mcp server selected, an **"Edit config…"** button shows beside the advisory (also shows before the file exists, so you can create it).
2. Click it. With no file yet, the raw editor is empty and the section combo shows `(top level)`. Pick a `[server]` section (type `[server]` in raw or use the combo once a section exists), set `role = admin` via the pick-list, **Save**.
3. Confirm: the dialog reports the backup name + **0600**; `ls -l` shows `-rw-------`; the "args inert" advisory now shows (that's #101 firing).
4. Re-open, change `port` (spinner) and `auth` (pick-list), add a `# comment` in the raw text, **Save**. Re-open → the comment survived and the values changed (surgical round-trip).
5. Confirm invalid enum values are structurally impossible via the pick-lists.
## Known limitations (by design; none block — noted for click-through)
- **Section-switch rebaselines the form.** Changing the section combo re-reads the pick-lists from raw text for the new section and resets the "changed since load" baseline. So pick-list edits made in section A, then abandoned by switching to section B *before Save*, are silently dropped. Fine for a first pass; the raw editor is always the source of truth.
- **Raw-text edits bypass schema validation, intentionally.** Save validates only the *form-changed* managed fields, not raw-editor content — typing `auth = "sshkey"` directly in raw and saving is **not** rejected. This is the deliberate reconciliation of "reject out-of-enum" vs "preserve unknown keys": whole-file validation would block saves on a pre-existing invalid/unknown key the user never touched. The **pick-list guarantee is form-only**; the raw editor is the power-user escape hatch.
- **First-pass editor scope.** One section targeted at a time; multi-profile editing is via the section picker + raw text. No comment-authoring UI (comments are preserved, not created). These are additive follow-ups if wanted.
## Nothing skipped / blocked
Both issues landed CI-green. No irreversible actions taken: no merges, no force-push, no tags, no pushes to `main`, no touches outside `bcc_core.py` / `bcc.py` / `tests/` (`requirements.txt` untouched — zero new deps). `apply_servers` unchanged; `ClientSpec`/`ServerSpec` unchanged (added on top).
## Cardinal-rule check
`apply_servers` still only writes `mcpServers`/`_disabledMcpServers`. The sidecar editor writes a **different file** through its **own** `write_sidecar`, which *reuses* `write_config`'s atomic-rename + `_make_backup` but never routes through `apply_servers` (there's an explicit test asserting no server keys leak into a sidecar write).
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.
Tracking issue for the 18 field notes from the 2026-08-02 ssh-mcp setup (
BCC-ROADMAP-ssh-mcp-notes-COWORK.md). Through-line: BCC models which host reads a config (ClientSpec) but not which server package it's editing. The seed we shipped (FLAG_ENV_MIGRATIONS, #89) grew into aServerSpecaxis; the rest falls out additively.Non-reversing rule: sidecar (TOML) editing is a new write target — its own writer reuses
write_config's atomic-rename +_make_backup; it does not go throughapply_servers.ClientSpecuntouched;ServerSpecorthogonal.Already shipped before this epic — no action
First cut — ✅ MERGED (#100)
Second cut — ✅ MERGED (#103, #104)
Follow-ups from the #102 click-through
Deferred (heavier / GUI-central — supervised)
~/.ssh/configCatalog-signing threat model settled separately — decision notes on #61 / #71.
Overnight cut — #90–#93 all implemented, PR'd, CI-green ✅
Worked the four filed issues unattended, one PR each off fresh
origin/main. Nothing merged, tagged, or released. Every PR stays open for review.feat/90mainfeat/91feat/90feat/92feat/90feat/93feat/91Stacked — #91/#92 are siblings off #90; #93 hangs off #91 (needs its sidecar path resolution). Each PR's base is its parent branch so per-PR diffs are clean. Merge order: #90 → #91 → #92 → #93 (merge #90 first; the rest retarget to
maincleanly). Prefer merge-commit/rebase, not squash (squashing a parent breaks a child's base).Suite: 509 → 529 passing (1 skipped).
ruff check+format --checkclean on all four. Touched onlybcc_core.py/bcc.py/tests/test_core.py. No catalog/signing/CI-workflow edits. No network calls; no sidecar-content writer added (cardinal rule intact —apply_serversstill writes onlymcpServers/_disabledMcpServers).Decisions / caveats to review
test_removed_flag_warnings_covers_migratable_and_manualencoded the old warn-only--sudoPassword. Verified facts + issue #90 mandate adding sudo/su →SSH_MCP_SUDO_PASSWORDto the auto-migrate map, so it now moves into env. The migration logic is byte-for-byte unchanged — only the registry data grew. All other #89 tests untouched.count_toml_profilesis a documented heuristic (notomllibon py3.10; exact ssh-mcp profile schema unverifiable here). It only advises, never edits/blocks — worth confirming against the real package schema.str(Path("/Users/…"))with backslashes; path assertions use.as_posix(). #91 and #93 each needed one follow-up commit for this — caught before declaring green.GUI needs a human click-through (headless smoke confirms wiring, not live firing — no ssh-mcp
config.tomlon the CI box)config.tomlpresent.<v>" button on annpx -y ssh-mcpserver (resolve → pin → badge flips to pinned; drift note when the cache moved past a pin).config.tomlat 0644 (POSIX only).#90 has no GUI (pure core). Full write-up in
OVERNIGHT-REPORT-COWORK.mdat the repo root.Overnight update — #101 and #102 landed (stacked, both CI-green, not merged).
Overnight report 2 — hot-reload (#101) + in-app sidecar editor (#102)
Two stacked PRs, both CI-green, non-destructive. Nothing merged.
PRs (recommended review/merge order: #101 → #102)
feat/101mainfeat/102feat/101Branches are stacked (not siblings), per the mandate:
feat/102is branched offfeat/101and its PR base is set tofeat/101, so #104's diff is only the editor. Merge #101 first; then #104's base auto-retargets tomain(or rebase). This avoids last round's silent sibling-merge conflict.Dependency decision — no new runtime dependency (this is the load-bearing call)
The task floated
tomli+tomli-wor a hand-roll usingtomllib/tomlifor reads. Neither survives the real CI constraint, which I discovered by reading.github/workflows/ci.yml(off-limits to edit):pip install pytest cryptography— notrequirements.txt.import bcc_corewith onlycryptographyinstalled.Therefore: (a)
bcc_coremust import with onlycryptographypresent → no top-level TOML import is possible; and (b) any pip TOML dep would leave the new core untested or red on CI, since CI never installs it and I can't change the workflow. Even the task's option (b) fails: thetomllibread path is untested on the 3.10 runner (no stdlibtomllib).Chosen: stdlib-only, uniform across 3.10/3.12/3.13 — a lenient scalar reader + a surgical line-editing writer that rewrites only the one key it's asked to. Comments, formatting, ordering and unknown keys/tables round-trip untouched — strictly safer than parse→dict→reserialize, and
tomli-wwouldn't comment-preserve anyway. Runtime deps stay exactlyPySide6+cryptography.requirements.txtunchanged.What shipped
#101 — hot-reload (PR #103)
sidecar_watch_paths(file + its dir + doc path, de-duped),sidecar_state_fingerprint(hashable snapshot folding #91 sidecar status + #93 permission status),sidecar_state_changed.QFileSystemWatcherover the selection's sidecar + BCC's own config, debounced 300 ms; re-arm on every event (atomic-rename replaces drop the inode); focus-in fallback viachangeEvent(ActivationChange);recheck_advisories()recomputes only warning labels (never field values, so it can't clobber unsaved edits); an external edit to BCC's own config shows a non-destructive Reload banner (confirm-on-dirty), never a silent overwrite.#102 — in-app sidecar editor (PR #104)
read_toml_section,toml_sections,set_toml_value/update_toml(surgical, CRLF-safe, escaping, create-section, delete-on-None),validate_sidecar_values(enum/port on managed fields only; unknown keys pass through),write_sidecar(atomic + rotating backup + chmod 0600/0700 via #93; neverapply_servers). Refactor:_make_backup/write_confignow share_atomic_write_text;_make_backupkeys on the file suffix (JSON naming byte-identical, so a.tomlsidecar and.jsonconfig keep separate backup pools).SidecarEditorDialog(section picker, enum pick-lists, range-bounded port spinner, raw-TOML fallback). Save applies only changed managed fields on top of the raw text, validates, writes, then re-runs advisories through #101's path so they update live.GUI elements needing a human click-through
CI has no PySide6, so none of the below is exercised by CI — I smoke-tested each headlessly (
QT_QPA_PLATFORM=offscreen: dialog build, section detection, changed-field diff, surgical save with comment preserved + 0600 applied, and Edit-button visibility). Please verify interactively:#101 — hot-reload
npx -y ssh-mcp --host=h --user=u), select it.mkdir -p ~/Library/Application\ Support/ssh-mcp && printf '[server]\nhost="h"\n' > ~/Library/Application\ Support/ssh-mcp/config.toml. Within ~1 s the "arguments are inert" advisory appears — no restart.chmod 644that file → the permission advisory appears;chmod 600→ it clears.config.toml→ advisory clears. Alt-tab away and back to confirm the focus-in fallback also refreshes.#102 — sidecar editor
(top level). Pick a[server]section (type[server]in raw or use the combo once a section exists), setrole = adminvia the pick-list, Save.ls -lshows-rw-------; the "args inert" advisory now shows (that's #101 firing).port(spinner) andauth(pick-list), add a# commentin the raw text, Save. Re-open → the comment survived and the values changed (surgical round-trip).Known limitations (by design; none block — noted for click-through)
auth = "sshkey"directly in raw and saving is not rejected. This is the deliberate reconciliation of "reject out-of-enum" vs "preserve unknown keys": whole-file validation would block saves on a pre-existing invalid/unknown key the user never touched. The pick-list guarantee is form-only; the raw editor is the power-user escape hatch.Nothing skipped / blocked
Both issues landed CI-green. No irreversible actions taken: no merges, no force-push, no tags, no pushes to
main, no touches outsidebcc_core.py/bcc.py/tests/(requirements.txtuntouched — zero new deps).apply_serversunchanged;ClientSpec/ServerSpecunchanged (added on top).Cardinal-rule check
apply_serversstill only writesmcpServers/_disabledMcpServers. The sidecar editor writes a different file through its ownwrite_sidecar, which reuseswrite_config's atomic-rename +_make_backupbut never routes throughapply_servers(there's an explicit test asserting no server keys leak into a sidecar write).