feat(settings): add update check and MCP uninstall
This commit is contained in:
12 files changed
+189
-9
No files matched your search
@@ -11,6 +11,8 @@ const MCP_PACKAGE_NAME: &str = "@dbx-app/mcp-server";
|
||||
const MCP_LATEST_URL: &str = "https://registry.npmjs.org/@dbx-app%2fmcp-server/latest";
|
||||
const MCP_INSTALL_COMMAND: &str = "npm install -g @dbx-app/mcp-server@latest";
|
||||
const MCP_PNPM_UPDATE_COMMAND: &str = "pnpm update -g @dbx-app/mcp-server";
|
||||
const MCP_UNINSTALL_COMMAND: &str = "npm uninstall -g @dbx-app/mcp-server";
|
||||
const MCP_PNPM_UNINSTALL_COMMAND: &str = "pnpm remove -g @dbx-app/mcp-server";
|
||||
const MCP_MIN_NODE_VERSION: NodeVersion = NodeVersion { major: 18, minor: 18, patch: 0 };
|
||||
const MCP_MIN_NODE_VERSION_REQUIREMENT: &str = ">=18.18.0";
|
||||
const SHELL_COMMAND_MARKER: &str = "__DBX_MCP_COMMAND_OUTPUT_START__";
|
||||
@@ -30,6 +32,7 @@ pub struct McpServerStatus {
|
||||
pub data_dir: Option<String>,
|
||||
pub install_command: String,
|
||||
pub update_command: String,
|
||||
pub uninstall_command: String,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
@@ -155,6 +158,13 @@ impl NodeRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
fn uninstall_command(&self) -> &'static str {
|
||||
match &self.package_manager {
|
||||
McpPackageManager::Npm => MCP_UNINSTALL_COMMAND,
|
||||
McpPackageManager::Pnpm { .. } => MCP_PNPM_UNINSTALL_COMMAND,
|
||||
}
|
||||
}
|
||||
|
||||
fn install_or_update(&self) -> Result<CommandOutput, String> {
|
||||
match &self.package_manager {
|
||||
McpPackageManager::Pnpm { command_path } if self.has_mcp_package() => {
|
||||
@@ -163,6 +173,15 @@ impl NodeRuntime {
|
||||
_ => self.npm_output(&["install", "-g", "@dbx-app/mcp-server@latest"]),
|
||||
}
|
||||
}
|
||||
|
||||
fn uninstall(&self) -> Result<CommandOutput, String> {
|
||||
match &self.package_manager {
|
||||
McpPackageManager::Pnpm { command_path } => {
|
||||
run_package_manager_command(command_path, &["remove", "-g", MCP_PACKAGE_NAME], &self.node_launcher_path)
|
||||
}
|
||||
McpPackageManager::Npm => self.npm_output(&["uninstall", "-g", MCP_PACKAGE_NAME]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@@ -221,6 +240,11 @@ pub async fn check_mcp_server_status(app: AppHandle) -> Result<McpServerStatus,
|
||||
data_dir,
|
||||
install_command: MCP_INSTALL_COMMAND.to_string(),
|
||||
update_command: runtime.as_ref().map(NodeRuntime::update_command).unwrap_or(MCP_INSTALL_COMMAND).to_string(),
|
||||
uninstall_command: runtime
|
||||
.as_ref()
|
||||
.map(NodeRuntime::uninstall_command)
|
||||
.unwrap_or(MCP_UNINSTALL_COMMAND)
|
||||
.to_string(),
|
||||
error,
|
||||
})
|
||||
}
|
||||
@@ -261,6 +285,31 @@ pub async fn install_mcp_server() -> Result<String, String> {
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn uninstall_mcp_server() -> Result<String, String> {
|
||||
tauri::async_runtime::spawn_blocking(|| {
|
||||
let runtime = resolve_node_runtime().ok_or_else(|| {
|
||||
format!("Unable to resolve a compatible Node.js ({}) and npm runtime.", MCP_MIN_NODE_VERSION_REQUIREMENT)
|
||||
})?;
|
||||
if !runtime.has_mcp_package() {
|
||||
return Err(format!("{} is not installed in the detected Node.js runtime.", MCP_PACKAGE_NAME));
|
||||
}
|
||||
|
||||
let output = runtime.uninstall()?;
|
||||
if !output.success {
|
||||
let error_msg = if !output.stderr.is_empty() { output.stderr } else { output.stdout };
|
||||
return Err(format!("Uninstallation failed: {}", error_msg));
|
||||
}
|
||||
|
||||
if runtime.refresh().is_some_and(|refreshed| refreshed.has_mcp_package()) {
|
||||
return Err(format!("Uninstallation completed, but {} is still installed.", MCP_PACKAGE_NAME));
|
||||
}
|
||||
Ok(format!("Successfully uninstalled {}", MCP_PACKAGE_NAME))
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
|
||||
async fn fetch_latest_mcp_version() -> Result<String, String> {
|
||||
let mut builder = reqwest::Client::builder().timeout(Duration::from_secs(10)).user_agent("dbx-mcp-status-checker");
|
||||
let proxy_url =
|
||||
@@ -1509,18 +1558,26 @@ mod tests {
|
||||
assert_eq!(probed.mcp_script_path, canonical_runtime_path(&script_path));
|
||||
assert_eq!(probed.mcp_version.as_deref(), Some("0.4.44"));
|
||||
assert_eq!(probed.update_command(), super::MCP_PNPM_UPDATE_COMMAND);
|
||||
assert_eq!(probed.uninstall_command(), super::MCP_PNPM_UNINSTALL_COMMAND);
|
||||
assert!(matches!(
|
||||
probed.package_manager,
|
||||
super::McpPackageManager::Pnpm { ref command_path } if command_path == &pnpm_path
|
||||
));
|
||||
let update_output = probed.install_or_update().unwrap();
|
||||
assert!(update_output.success);
|
||||
let pnpm_log = std::fs::read_to_string(pnpm_log_path).unwrap();
|
||||
let pnpm_log = std::fs::read_to_string(&pnpm_log_path).unwrap();
|
||||
assert!(pnpm_log.contains("ARGS=update -g @dbx-app/mcp-server\n"));
|
||||
assert!(!pnpm_log.contains("--registry"));
|
||||
assert!(pnpm_log.contains(&format!("PNPM_HOME={}", dir.display())));
|
||||
assert!(pnpm_log.contains(&format!("PATH={}", bin_dir.display())));
|
||||
|
||||
let uninstall_output = probed.uninstall().unwrap();
|
||||
assert!(uninstall_output.success);
|
||||
let pnpm_log = std::fs::read_to_string(&pnpm_log_path).unwrap();
|
||||
assert!(pnpm_log.contains("ARGS=remove -g @dbx-app/mcp-server"));
|
||||
assert!(pnpm_log.contains(&format!("PNPM_HOME={}", dir.display())));
|
||||
assert!(pnpm_log.contains(&format!("PATH={}", bin_dir.display())));
|
||||
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
|
||||
@@ -2205,6 +2205,7 @@ pub fn run() {
|
||||
commands::history::delete_history_entry,
|
||||
commands::mcp::check_mcp_server_status,
|
||||
commands::mcp::install_mcp_server,
|
||||
commands::mcp::uninstall_mcp_server,
|
||||
commands::update::check_for_updates,
|
||||
commands::update::fetch_changelog,
|
||||
commands::update::get_system_proxy_url,
|
||||
|
||||
Reference in new issue
Block a user