feat(#88): detect & migrate removed CLI flags into env (ssh-mcp v2)
CI / Lint (ruff) (pull_request) Successful in 11s
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 38s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 12s
CI / Catalog signature (pull_request) Successful in 8s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 12s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 12s

ssh-mcp v2 removed --password from the command line and reads
SSH_MCP_PASSWORD instead, so an old config crashes on startup. Add a
data-driven FLAG_ENV_MIGRATIONS registry plus detect_migratable_package,
migrate_removed_flags and removed_flag_warnings in bcc_core, and a
'Fix: move to environment variables' one-click action + warning in the
stdio server editor, with a matching main-window lint line. The literal
value lands in env{} (the only form Claude Desktop honours). 14 tests.
This commit is contained in:
the_og
2026-08-11 16:02:11 +00:00
committed by claude
parent 072a5cdc08
commit 7368dcdbff
3 changed files with 395 additions and 0 deletions
+49
View File
@@ -602,6 +602,19 @@ class ServerEditor(QFrame):
self.secret_warn.setWordWrap(True)
self.secret_warn.hide()
v.addWidget(self.secret_warn)
# Shown when args carry a CLI flag a package removed in a major version
# (e.g. ssh-mcp v2's --password), with a one-click move into env.
self.removed_flag_warn = QLabel("")
self.removed_flag_warn.setStyleSheet(f"color: {WARN};")
self.removed_flag_warn.setWordWrap(True)
self.removed_flag_warn.hide()
self.removed_flag_fix_btn = QPushButton("Fix: move to environment variables")
self.removed_flag_fix_btn.clicked.connect(self._fix_removed_flags)
self.removed_flag_fix_btn.hide()
rf_row = QHBoxLayout()
rf_row.addWidget(self.removed_flag_warn, 1)
rf_row.addWidget(self.removed_flag_fix_btn)
v.addLayout(rf_row)
v.addWidget(self._lbl("Environment variables"))
self.env = KeyValueTable(
"Variable", "Value", on_change=self._emit, before_change=self._before_change
@@ -649,6 +662,8 @@ class ServerEditor(QFrame):
self.args_warn.hide()
self.args_fix_btn.hide()
self.secret_warn.hide()
self.removed_flag_warn.hide()
self.removed_flag_fix_btn.hide()
self._loading = False
return
self.setEnabled(True)
@@ -736,6 +751,8 @@ class ServerEditor(QFrame):
self.args_warn.hide()
self.args_fix_btn.hide()
self.secret_warn.hide()
self.removed_flag_warn.hide()
self.removed_flag_fix_btn.hide()
return
_, notes = core.split_suspicious_args(self._current_arg_lines())
if notes:
@@ -751,6 +768,23 @@ class ServerEditor(QFrame):
self.secret_warn.show()
else:
self.secret_warn.hide()
# Flags a package removed across a major version (e.g. ssh-mcp v2's
# --password). Needs command + env too, so build from the live form.
stdio = {
"command": self.command.text().strip(),
"args": self._current_arg_lines(),
"env": self.env.dump(),
}
rf_warnings = core.removed_flag_warnings(stdio)
if rf_warnings:
self.removed_flag_warn.setText("⚠ " + "\n".join(rf_warnings))
self.removed_flag_warn.show()
# Only offer the button when something is actually auto-migratable.
migrated, _ = core.migrate_removed_flags(stdio)
self.removed_flag_fix_btn.setVisible(migrated is not stdio)
else:
self.removed_flag_warn.hide()
self.removed_flag_fix_btn.hide()
def _fix_args(self):
if self._before_change:
@@ -758,6 +792,19 @@ class ServerEditor(QFrame):
fixed, _ = core.split_suspicious_args(self._current_arg_lines())
self.args.setPlainText("\n".join(fixed)) # triggers _emit -> recheck
def _fix_removed_flags(self):
if self._before_change:
self._before_change()
stdio = {
"command": self.command.text().strip(),
"args": self._current_arg_lines(),
"env": self.env.dump(),
}
migrated, _ = core.migrate_removed_flags(stdio)
# Reload env first, then args; setting args text triggers _emit -> recheck.
self.env.load(migrated.get("env", {}))
self.args.setPlainText("\n".join(migrated.get("args", [])))
# --- dependency ------------------------------------------------------ #
def refresh_dependency(self, auto_open=False):
if not self.isEnabled():
@@ -2633,6 +2680,8 @@ class MainWindow(QMainWindow):
for entry in self.servers:
for warning in core.env_ref_warnings(entry.data, self.current_profile):
lint_warnings.append(f"'{entry.name}': {warning}")
for warning in core.removed_flag_warnings(entry.data):
lint_warnings.append(f"'{entry.name}': {warning}")
if lint_warnings:
self.validation_lbl.setText(f"⚠ {lint_warnings[0]}")
self.validation_lbl.setStyleSheet(f"color: {WARN};")