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
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:
@@ -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};")
|
||||
|
||||
Reference in New Issue
Block a user