ssh-mcp refuses to start if its config is group/world-readable — it throws when mode & 0o077 is set, requiring dir 0700 / file 0600. A GUI user has no idea what chmod 600 means; they just get a dead server. This pre-flights the mode and offers a one-click fix. Generalised so any credential-bearing config can be checked.
Core (pure bcc_core, POSIX-only, fully injectable — tests never touch a real file)
permission_status(path) — the ssh-mcp rule: any group/other bit set (mode & 0o077) is not-ok; the file must be 0600, its directory 0700. Returns None on Windows (POSIX modes don't apply) or when the file is absent (nothing to pre-flight). Emits plain-language problems that name the offending octal mode. stat_mode is injectable.
fix_permissions(path) — chmod the file to 0600 and its directory to 0700. No-op on Windows; reports an OSError rather than raising. chmod is injectable.
sidecar_permission_warnings() / sidecar_permission_fix_target() — tie the check to #91's sidecar path resolution so it knows which file to inspect. Mirror the sidecar-warnings shape.
GUI (minimal)
A warning label + "Fix permissions" button in the stdio editor (mirrors the removed-flag surface), shown only when the sidecar exists and is too open. Hidden on Windows and for non-sidecar servers. The fix chmods the user's own config in place (their explicit click); a failure is surfaced in-place. Smoke-tested headlessly with QT_QPA_PLATFORM=offscreen.
Testing
ruff + format --check clean; pytest green (529 passed). Fixture-driven tests: 0600 ok, 0644 file+dir blocked with octal in the message, file-bad/dir-ok, Windows + missing-file → None, chmod fix (file+dir), Windows no-op, OSError reporting, and the sidecar-path integration + fix target.
Stacked PR — base is feat/91 (depends on #91's sidecar path resolution to know which file to check). Merge order: #90 → #91 → #93. 🤖 Generated with Claude Code
## What (P1 — validation)
`ssh-mcp` refuses to start if its config is group/world-readable — it throws when `mode & 0o077` is set, requiring dir `0700` / file `0600`. A GUI user has no idea what `chmod 600` means; they just get a dead server. This pre-flights the mode and offers a one-click fix. Generalised so **any** credential-bearing config can be checked.
### Core (pure `bcc_core`, POSIX-only, fully injectable — tests never touch a real file)
- **`permission_status(path)`** — the ssh-mcp rule: any group/other bit set (`mode & 0o077`) is not-ok; the file must be `0600`, its directory `0700`. Returns `None` on Windows (POSIX modes don't apply) or when the file is absent (nothing to pre-flight). Emits plain-language problems that name the offending octal mode. `stat_mode` is injectable.
- **`fix_permissions(path)`** — chmod the file to `0600` and its directory to `0700`. No-op on Windows; reports an `OSError` rather than raising. `chmod` is injectable.
- **`sidecar_permission_warnings()` / `sidecar_permission_fix_target()`** — tie the check to **#91's** sidecar path resolution so it knows *which* file to inspect. Mirror the sidecar-warnings shape.
### GUI (minimal)
A warning label + **"Fix permissions"** button in the stdio editor (mirrors the removed-flag surface), shown only when the sidecar exists and is too open. Hidden on Windows and for non-sidecar servers. The fix chmods the user's own config in place (their explicit click); a failure is surfaced in-place. Smoke-tested headlessly with `QT_QPA_PLATFORM=offscreen`.
### Testing
`ruff` + `format --check` clean; `pytest` green (529 passed). Fixture-driven tests: 0600 ok, 0644 file+dir blocked with octal in the message, file-bad/dir-ok, Windows + missing-file → `None`, chmod fix (file+dir), Windows no-op, OSError reporting, and the sidecar-path integration + fix target.
Closes #93. Part of epic #94.
---
**Stacked PR — base is `feat/91`** (depends on #91's sidecar path resolution to know which file to check). Merge order: **#90 → #91 → #93**.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
ssh-mcp refuses to start if its config is group/world-readable (mode & 0o077 →
throws, requiring dir 0700 / file 0600). A GUI user has no idea what chmod 600
means — they just get a dead server. This checks it and offers a one-click fix.
Core (pure, POSIX-only, injectable stat/chmod so tests never touch a real file):
- permission_status(path): the ssh-mcp rule — any group/other bit set (mode & 0o077)
is not-ok; file must be 0600, its dir 0700. Returns None on Windows (modes don't
apply) or when the file is absent (nothing to pre-flight). Plain-language problems
naming the offending octal mode.
- fix_permissions(path): chmod file → 0600, dir → 0700. No-op on Windows; reports
an OSError instead of raising.
- sidecar_permission_warnings() / sidecar_permission_fix_target(): tie the check to
#91's sidecar path resolution so it knows WHICH file to inspect. Mirror the
sidecar-warnings shape.
GUI: a warning label + "Fix permissions" button in the stdio editor (mirrors the
removed-flag surface), shown only when the sidecar exists and is too open. Hidden
on Windows and for non-sidecar servers. Smoke-tested headlessly.
pytest green (529 passed), ruff + format clean. Closes#93. Part of epic #94.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
permission_status/fix_permissions wrap paths in the running OS's Path class, so
on the windows-latest CI job str(p) uses backslashes and the fixture dict lookups
miss — the same portability trap fixed for #91. Normalise with Path(p).as_posix()
in the four affected lambdas. Pure test fix; production code unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
What (P1 — validation)
ssh-mcprefuses to start if its config is group/world-readable — it throws whenmode & 0o077is set, requiring dir0700/ file0600. A GUI user has no idea whatchmod 600means; they just get a dead server. This pre-flights the mode and offers a one-click fix. Generalised so any credential-bearing config can be checked.Core (pure
bcc_core, POSIX-only, fully injectable — tests never touch a real file)permission_status(path)— the ssh-mcp rule: any group/other bit set (mode & 0o077) is not-ok; the file must be0600, its directory0700. ReturnsNoneon Windows (POSIX modes don't apply) or when the file is absent (nothing to pre-flight). Emits plain-language problems that name the offending octal mode.stat_modeis injectable.fix_permissions(path)— chmod the file to0600and its directory to0700. No-op on Windows; reports anOSErrorrather than raising.chmodis injectable.sidecar_permission_warnings()/sidecar_permission_fix_target()— tie the check to #91's sidecar path resolution so it knows which file to inspect. Mirror the sidecar-warnings shape.GUI (minimal)
A warning label + "Fix permissions" button in the stdio editor (mirrors the removed-flag surface), shown only when the sidecar exists and is too open. Hidden on Windows and for non-sidecar servers. The fix chmods the user's own config in place (their explicit click); a failure is surfaced in-place. Smoke-tested headlessly with
QT_QPA_PLATFORM=offscreen.Testing
ruff+format --checkclean;pytestgreen (529 passed). Fixture-driven tests: 0600 ok, 0644 file+dir blocked with octal in the message, file-bad/dir-ok, Windows + missing-file →None, chmod fix (file+dir), Windows no-op, OSError reporting, and the sidecar-path integration + fix target.Closes #93. Part of epic #94.
Stacked PR — base is
feat/91(depends on #91's sidecar path resolution to know which file to check). Merge order: #90 → #91 → #93.🤖 Generated with Claude Code
Pull request closed