diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index a33d87bef..8e3df36e5 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -1719,16 +1719,18 @@ const mcpLaunchConfig = computed(() => { }, }; } + const env = mcpStatus.value?.data_dir ? { DBX_DATA_DIR: mcpStatus.value.data_dir } : undefined; if (mcpStatus.value?.node_path && mcpStatus.value.script_path) { return { command: mcpStatus.value.node_path, args: [mcpStatus.value.script_path], + env, }; } if (mcpStatus.value?.bin_path) { - return { command: mcpStatus.value.bin_path }; + return { command: mcpStatus.value.bin_path, env }; } - return undefined; + return env ? { command: "dbx-mcp-server", env } : undefined; }); const mcpJsonRecommendedConfig = computed(() => buildMcpJsonConfig(mcpLaunchConfig.value)); diff --git a/apps/desktop/src/lib/__tests__/mcp/mcpConfigTemplates.spec.ts b/apps/desktop/src/lib/__tests__/mcp/mcpConfigTemplates.spec.ts index 199d87d8a..5c0a3e38f 100644 --- a/apps/desktop/src/lib/__tests__/mcp/mcpConfigTemplates.spec.ts +++ b/apps/desktop/src/lib/__tests__/mcp/mcpConfigTemplates.spec.ts @@ -31,11 +31,12 @@ describe("MCP config templates", () => { const nodeLaunch = { command: "C:\\Program Files\\nodejs\\node.exe", args: ["C:\\Users\\supervisor\\AppData\\Roaming\\npm\\node_modules\\@dbx-app\\mcp-server\\bin\\dbx-mcp-server.js"], + env: { DBX_DATA_DIR: "D:\\GreenSoft\\DBX\\data" }, }; const nativeBinPath = "C:\\Users\\supervisor\\AppData\\Roaming\\npm\\node_modules\\@dbx-app\\mcp-win32-x64\\bin\\dbx-mcp.exe"; expect(JSON.parse(buildMcpTraeConfig(nodeLaunch, nativeBinPath))).toEqual({ - mcpServers: { dbx: { command: nativeBinPath } }, + mcpServers: { dbx: { command: nativeBinPath, env: nodeLaunch.env } }, }); expect(JSON.parse(buildMcpTraeConfig(nodeLaunch))).toEqual({ mcpServers: { dbx: nodeLaunch }, @@ -59,6 +60,16 @@ describe("MCP config templates", () => { expect(buildMcpJsonConfig(launch)).not.toContain("DBX_MCP_ALLOW_WRITES"); }); + it("includes the portable DBX data directory in JSON and Codex configs", () => { + const launch = { + command: "dbx-mcp-server", + env: { DBX_DATA_DIR: "D:\\GreenSoft\\DBX\\data" }, + }; + + expect(JSON.parse(buildMcpJsonConfig(launch)).mcpServers.dbx.env).toEqual(launch.env); + expect(buildMcpCodexConfig(launch)).toContain('DBX_DATA_DIR = "D:\\\\GreenSoft\\\\DBX\\\\data"'); + }); + it("keeps a deployed Web base path in DBX_WEB_URL", () => { expect(mcpWebBackendUrl("https://dbx.example.com", "/tools/dbx/api")).toBe("https://dbx.example.com/tools/dbx"); }); diff --git a/apps/desktop/src/lib/backend/http.ts b/apps/desktop/src/lib/backend/http.ts index 299994399..b6be9e9a3 100644 --- a/apps/desktop/src/lib/backend/http.ts +++ b/apps/desktop/src/lib/backend/http.ts @@ -3288,6 +3288,7 @@ export async function checkMcpServerStatus(): Promise, pub native_bin_path: Option, pub script_path: Option, + pub data_dir: Option, pub install_command: String, pub update_command: String, pub error: Option, @@ -178,7 +180,9 @@ struct NodeVersion { } #[tauri::command] -pub async fn check_mcp_server_status() -> Result { +pub async fn check_mcp_server_status(app: AppHandle) -> Result { + let default_data_dir = app.path().app_data_dir().map_err(|error| error.to_string())?; + let data_dir = crate::data_dir::resolve_data_dir_with_mode(default_data_dir).custom_data_dir().map(path_string); let local_status = tauri::async_runtime::spawn_blocking(|| { let runtime = resolve_node_runtime(); let fallback_bin = match runtime.as_ref() { @@ -221,6 +225,7 @@ pub async fn check_mcp_server_status() -> Result { bin_path, native_bin_path, script_path, + 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(), error, diff --git a/src-tauri/src/data_dir.rs b/src-tauri/src/data_dir.rs index 599bc5b88..56bdf551c 100644 --- a/src-tauri/src/data_dir.rs +++ b/src-tauri/src/data_dir.rs @@ -25,6 +25,10 @@ impl DataDirResolution { matches!(self.mode, DataDirMode::EnvOverride | DataDirMode::Portable { .. }) } + pub fn custom_data_dir(&self) -> Option<&Path> { + self.uses_custom_data_dir().then_some(self.data_dir.as_path()) + } + pub fn is_portable_mode(&self) -> bool { matches!(self.mode, DataDirMode::Portable { .. }) } @@ -138,6 +142,7 @@ mod tests { let resolution = resolve_data_dir_from_inputs(default_dir, Some(exe_dir.clone()), true, false, None); assert_eq!(resolution.data_dir, exe_dir.join("data")); + assert_eq!(resolution.custom_data_dir(), Some(resolution.data_dir.as_path())); assert_eq!(resolution.mode, DataDirMode::Portable { exe_dir }); assert!(resolution.uses_custom_data_dir()); assert!(resolution.is_portable_mode()); @@ -151,6 +156,7 @@ mod tests { let resolution = resolve_data_dir_from_inputs(default_dir.clone(), Some(exe_dir), true, true, None); assert_eq!(resolution.data_dir, default_dir); + assert_eq!(resolution.custom_data_dir(), None); assert_eq!(resolution.mode, DataDirMode::Default); assert!(!resolution.uses_custom_data_dir()); assert!(!resolution.is_portable_mode()); @@ -165,6 +171,7 @@ mod tests { let resolution = resolve_data_dir_from_inputs(default_dir, Some(exe_dir), true, true, Some(env_dir.clone())); assert_eq!(resolution.data_dir, env_dir); + assert_eq!(resolution.custom_data_dir(), Some(resolution.data_dir.as_path())); assert_eq!(resolution.mode, DataDirMode::EnvOverride); assert!(resolution.uses_custom_data_dir()); assert!(!resolution.is_portable_mode());