fix(mcp): use native binary for TRAE on Windows
This commit is contained in:
parent
4e68cd84cc
commit
01fde8879f
|
|
@ -95,9 +95,9 @@ import { currentExecutableStatementRange, type SqlTextRange } from "@/lib/sql/sq
|
|||
import { executableStatementRangeCacheForDoc, executableStatementRangeStartingAt, type ExecutableStatementRangeCache } from "@/lib/sql/executableStatementRangeCache";
|
||||
import { EMPTY_TABLE_COLUMN_TEMPLATE_DATA_TYPE, parseTableColumnTemplateFields, TABLE_COLUMN_TEMPLATE_DATABASE_TYPES } from "@/lib/table/tableColumnTemplates";
|
||||
import { DEFAULT_SQL_VARIABLE_SYNTAX_TOGGLES, normalizeSqlVariableSyntaxOverrides, SQL_VARIABLE_SYNTAX_DATABASE_TYPES, SQL_VARIABLE_SYNTAX_KEYS, SQL_VARIABLE_SYNTAX_TOKENS, type SqlVariableSyntaxOverrides, type SqlVariableSyntaxToggles } from "@/lib/sql/sqlVariableSyntax";
|
||||
import { buildMcpCherryStudioConfig, buildMcpCodexConfig, buildMcpJsonConfig, buildMcpOpenCodeConfig, buildMcpVsCodeConfig, mcpWebBackendUrl, type McpLaunchConfig } from "@/lib/mcp/mcpConfigTemplates";
|
||||
import { buildMcpCherryStudioConfig, buildMcpCodexConfig, buildMcpJsonConfig, buildMcpOpenCodeConfig, buildMcpTraeConfig, buildMcpVsCodeConfig, mcpWebBackendUrl, type McpLaunchConfig } from "@/lib/mcp/mcpConfigTemplates";
|
||||
import { isMcpPolicyMutationBlocked, MCP_CAPABILITY_ROWS, MCP_EXECUTION_MODE_COLUMNS, mcpExecutionModeFromPolicy, mcpPolicyFieldsForExecutionMode, type McpExecutionMode } from "@/lib/mcp/mcpPolicySelection";
|
||||
import { isMacOS } from "@/lib/backend/platform";
|
||||
import { isMacOS, isWindows } from "@/lib/backend/platform";
|
||||
import { combineDataTypeForDatabase, dataTypeLengthInputValue, getDataTypeOptions, getDefaultLengthForType, isDataTypeLengthDisabled, splitDataType } from "@/lib/table/tableStructureEditorState";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import type { DatabaseType, SqlSnippet } from "@/types/database";
|
||||
|
|
@ -1509,6 +1509,12 @@ const mcpLaunchConfig = computed<McpLaunchConfig | undefined>(() => {
|
|||
|
||||
const mcpJsonRecommendedConfig = computed(() => buildMcpJsonConfig(mcpLaunchConfig.value));
|
||||
|
||||
const mcpTraeRecommendedConfig = computed(() => {
|
||||
// TRAE currently splits Windows executable paths containing spaces, so bypass Node and launch the native MCP binary directly.
|
||||
const nativeBinPath = !isWeb && isWindows() ? mcpStatus.value?.native_bin_path : undefined;
|
||||
return buildMcpTraeConfig(mcpLaunchConfig.value, nativeBinPath ?? undefined);
|
||||
});
|
||||
|
||||
const mcpVsCodeRecommendedConfig = computed(() => buildMcpVsCodeConfig(mcpLaunchConfig.value));
|
||||
|
||||
const mcpCherryStudioRecommendedConfig = computed(() => buildMcpCherryStudioConfig(mcpLaunchConfig.value));
|
||||
|
|
@ -5055,8 +5061,8 @@ onUnmounted(cleanupPreviewEditor);
|
|||
{{ t("settings.mcpTraeConfigPath") }}
|
||||
</div>
|
||||
<div class="relative rounded-md border bg-background p-3">
|
||||
<pre class="overflow-x-auto whitespace-pre text-xs leading-relaxed"><code>{{ mcpJsonRecommendedConfig }}</code></pre>
|
||||
<Button type="button" variant="outline" size="icon" class="absolute right-2 top-2 h-7 w-7" :title="t('common.copy')" @click="copyMcpText('trae-config', mcpJsonRecommendedConfig)">
|
||||
<pre class="overflow-x-auto whitespace-pre text-xs leading-relaxed"><code>{{ mcpTraeRecommendedConfig }}</code></pre>
|
||||
<Button type="button" variant="outline" size="icon" class="absolute right-2 top-2 h-7 w-7" :title="t('common.copy')" @click="copyMcpText('trae-config', mcpTraeRecommendedConfig)">
|
||||
<CheckCircle2 v-if="mcpCopied === 'trae-config'" class="h-3.5 w-3.5 text-green-500" />
|
||||
<Copy v-else class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { buildMcpCherryStudioConfig, buildMcpCodexConfig, buildMcpJsonConfig, buildMcpOpenCodeConfig, buildMcpVsCodeConfig, mcpWebBackendUrl } from "@/lib/mcp/mcpConfigTemplates";
|
||||
import { buildMcpCherryStudioConfig, buildMcpCodexConfig, buildMcpJsonConfig, buildMcpOpenCodeConfig, buildMcpTraeConfig, buildMcpVsCodeConfig, mcpWebBackendUrl } from "@/lib/mcp/mcpConfigTemplates";
|
||||
|
||||
describe("MCP config templates", () => {
|
||||
it("builds the standard mcpServers JSON used by Claude, Cursor, TRAE, and Windsurf", () => {
|
||||
|
|
@ -27,6 +27,21 @@ describe("MCP config templates", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("uses the native binary for TRAE when Windows Node lives under Program Files", () => {
|
||||
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"],
|
||||
};
|
||||
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 } },
|
||||
});
|
||||
expect(JSON.parse(buildMcpTraeConfig(nodeLaunch))).toEqual({
|
||||
mcpServers: { dbx: nodeLaunch },
|
||||
});
|
||||
});
|
||||
|
||||
it("includes Web runtime settings without restoring permission environment variables", () => {
|
||||
const launch = {
|
||||
command: "dbx-mcp-server",
|
||||
|
|
|
|||
|
|
@ -2306,6 +2306,7 @@ export async function checkMcpServerStatus(): Promise<import("@/lib/backend/taur
|
|||
latest_version: null,
|
||||
update_available: false,
|
||||
bin_path: null,
|
||||
native_bin_path: null,
|
||||
script_path: null,
|
||||
install_command: "npm install -g @dbx-app/mcp-server@latest --registry=https://registry.npmjs.org",
|
||||
update_command: "npm install -g @dbx-app/mcp-server@latest --registry=https://registry.npmjs.org",
|
||||
|
|
|
|||
|
|
@ -1449,6 +1449,7 @@ export interface McpServerStatus {
|
|||
latest_version: string | null;
|
||||
update_available: boolean;
|
||||
bin_path: string | null;
|
||||
native_bin_path: string | null;
|
||||
script_path: string | null;
|
||||
install_command: string;
|
||||
update_command: string;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ export function buildMcpJsonConfig(config?: McpLaunchConfig): string {
|
|||
return JSON.stringify({ mcpServers: { dbx } }, null, 2);
|
||||
}
|
||||
|
||||
export function buildMcpTraeConfig(config?: McpLaunchConfig, nativeBinPath?: string): string {
|
||||
return buildMcpJsonConfig(nativeBinPath ? { command: nativeBinPath } : config);
|
||||
}
|
||||
|
||||
export function buildMcpVsCodeConfig(config?: McpLaunchConfig): string {
|
||||
const dbx: Record<string, unknown> = {
|
||||
type: "stdio",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ pub struct McpServerStatus {
|
|||
pub latest_version: Option<String>,
|
||||
pub update_available: bool,
|
||||
pub bin_path: Option<String>,
|
||||
pub native_bin_path: Option<String>,
|
||||
pub script_path: Option<String>,
|
||||
pub install_command: String,
|
||||
pub update_command: String,
|
||||
|
|
@ -48,6 +49,7 @@ struct NodeRuntime {
|
|||
mcp_version: Option<String>,
|
||||
mcp_script_path: Option<PathBuf>,
|
||||
mcp_bin_path: Option<PathBuf>,
|
||||
mcp_native_bin_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -81,8 +83,20 @@ impl NodeRuntime {
|
|||
// Resolve the package-declared launcher so npm layout changes do not break the built-in AI assistant.
|
||||
let mcp_script_path = package.filter(|_| package_is_compatible).map(|package| package.script_path);
|
||||
let mcp_bin_path = mcp_bin_path(&npm_prefix);
|
||||
// TRAE on Windows splits executable paths containing spaces, so expose the native package binary as a safe direct launch option.
|
||||
let mcp_native_bin_path =
|
||||
package_is_compatible.then(|| mcp_native_binary_path(&package_root, &npm_root)).flatten();
|
||||
|
||||
Some(Self { node_path, npm_cli_path, npm_root, node_version, mcp_version, mcp_script_path, mcp_bin_path })
|
||||
Some(Self {
|
||||
node_path,
|
||||
npm_cli_path,
|
||||
npm_root,
|
||||
node_version,
|
||||
mcp_version,
|
||||
mcp_script_path,
|
||||
mcp_bin_path,
|
||||
mcp_native_bin_path,
|
||||
})
|
||||
}
|
||||
|
||||
fn has_mcp_package(&self) -> bool {
|
||||
|
|
@ -124,6 +138,8 @@ pub async fn check_mcp_server_status() -> Result<McpServerStatus, String> {
|
|||
let current_version = runtime.as_ref().and_then(|runtime| runtime.mcp_version.clone());
|
||||
let script_path =
|
||||
runtime.as_ref().and_then(|runtime| runtime.mcp_script_path.as_ref()).map(|path| path_string(path));
|
||||
let native_bin_path =
|
||||
runtime.as_ref().and_then(|runtime| runtime.mcp_native_bin_path.as_ref()).map(|path| path_string(path));
|
||||
let bin_path = fallback_bin.as_ref().map(|path| path_string(path));
|
||||
let latest_version = latest_version.ok();
|
||||
let update_available = current_version
|
||||
|
|
@ -145,6 +161,7 @@ pub async fn check_mcp_server_status() -> Result<McpServerStatus, String> {
|
|||
latest_version,
|
||||
update_available,
|
||||
bin_path,
|
||||
native_bin_path,
|
||||
script_path,
|
||||
install_command: MCP_INSTALL_COMMAND.to_string(),
|
||||
update_command: MCP_INSTALL_COMMAND.to_string(),
|
||||
|
|
@ -562,6 +579,43 @@ fn mcp_package(package_root: &Path) -> Option<McpPackage> {
|
|||
Some(McpPackage { version, script_path, minimum_node_version })
|
||||
}
|
||||
|
||||
fn mcp_native_binary_path(package_root: &Path, npm_root: &Path) -> Option<PathBuf> {
|
||||
let (package_name, binary_name) = mcp_native_package()?;
|
||||
mcp_native_binary_path_for(package_root, npm_root, package_name, binary_name)
|
||||
}
|
||||
|
||||
fn mcp_native_binary_path_for(
|
||||
package_root: &Path,
|
||||
npm_root: &Path,
|
||||
package_name: &str,
|
||||
binary_name: &str,
|
||||
) -> Option<PathBuf> {
|
||||
[
|
||||
package_root.join("node_modules").join(package_name).join("bin").join(binary_name),
|
||||
npm_root.join(package_name).join("bin").join(binary_name),
|
||||
]
|
||||
.into_iter()
|
||||
.find_map(|path| canonical_runtime_path(&path))
|
||||
}
|
||||
|
||||
fn mcp_native_package() -> Option<(&'static str, &'static str)> {
|
||||
if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
|
||||
Some(("@dbx-app/mcp-darwin-arm64", "dbx-mcp"))
|
||||
} else if cfg!(all(target_os = "macos", target_arch = "x86_64")) {
|
||||
Some(("@dbx-app/mcp-darwin-x64", "dbx-mcp"))
|
||||
} else if cfg!(all(target_os = "linux", target_arch = "aarch64")) {
|
||||
Some(("@dbx-app/mcp-linux-arm64-gnu", "dbx-mcp"))
|
||||
} else if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
|
||||
Some(("@dbx-app/mcp-linux-x64-gnu", "dbx-mcp"))
|
||||
} else if cfg!(all(target_os = "windows", target_arch = "aarch64")) {
|
||||
Some(("@dbx-app/mcp-win32-arm64", "dbx-mcp.exe"))
|
||||
} else if cfg!(all(target_os = "windows", target_arch = "x86_64")) {
|
||||
Some(("@dbx-app/mcp-win32-x64", "dbx-mcp.exe"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_minimum_node_version(requirement: &str) -> Option<NodeVersion> {
|
||||
let version = requirement.trim().strip_prefix(">=")?.split_whitespace().next()?;
|
||||
parse_node_version(version)
|
||||
|
|
@ -854,10 +908,10 @@ mod tests {
|
|||
#[cfg(not(windows))]
|
||||
use super::{bash_login_script, canonical_runtime_path, NodeRuntimeCandidate};
|
||||
use super::{
|
||||
is_mcp_compatible_node_version, mcp_command_for_runtime, mcp_package, normalized_reported_path,
|
||||
npm_cli_candidates, parse_minimum_node_version, parse_node_version, prefer_runtime, prefixed_output_path,
|
||||
require_managed_mcp_command, resolve_managed_mcp_command, stdout_after_shell_marker, NodeRuntime, NodeVersion,
|
||||
MCP_MIN_NODE_VERSION_REQUIREMENT, MCP_PACKAGE_NAME, SHELL_COMMAND_MARKER,
|
||||
is_mcp_compatible_node_version, mcp_command_for_runtime, mcp_native_binary_path_for, mcp_package,
|
||||
normalized_reported_path, npm_cli_candidates, parse_minimum_node_version, parse_node_version, prefer_runtime,
|
||||
prefixed_output_path, require_managed_mcp_command, resolve_managed_mcp_command, stdout_after_shell_marker,
|
||||
NodeRuntime, NodeVersion, MCP_MIN_NODE_VERSION_REQUIREMENT, MCP_PACKAGE_NAME, SHELL_COMMAND_MARKER,
|
||||
};
|
||||
#[cfg(not(windows))]
|
||||
use super::{shell_command_script, shell_quote};
|
||||
|
|
@ -881,6 +935,7 @@ mod tests {
|
|||
mcp_version: script_path.map(|_| "0.4.29".to_string()),
|
||||
mcp_script_path: script_path.map(PathBuf::from),
|
||||
mcp_bin_path: None,
|
||||
mcp_native_bin_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1019,6 +1074,38 @@ mod tests {
|
|||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_binary_resolves_nested_and_hoisted_optional_packages() {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
let nonce = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos();
|
||||
let dir = std::env::temp_dir().join(format!("dbx-mcp-native-package-test-{}-{nonce}", std::process::id()));
|
||||
let npm_root = dir.join("node_modules");
|
||||
let package_root = npm_root.join("@dbx-app").join("mcp-server");
|
||||
let package_name = "@dbx-app/mcp-win32-x64";
|
||||
let binary_name = "dbx-mcp.exe";
|
||||
let nested_binary = package_root.join("node_modules").join(package_name).join("bin").join(binary_name);
|
||||
std::fs::create_dir_all(nested_binary.parent().unwrap()).unwrap();
|
||||
std::fs::write(&nested_binary, "nested binary").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
mcp_native_binary_path_for(&package_root, &npm_root, package_name, binary_name),
|
||||
canonical_runtime_path(&nested_binary)
|
||||
);
|
||||
|
||||
std::fs::remove_file(&nested_binary).unwrap();
|
||||
let hoisted_binary = npm_root.join(package_name).join("bin").join(binary_name);
|
||||
std::fs::create_dir_all(hoisted_binary.parent().unwrap()).unwrap();
|
||||
std::fs::write(&hoisted_binary, "hoisted binary").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
mcp_native_binary_path_for(&package_root, &npm_root, package_name, binary_name),
|
||||
canonical_runtime_path(&hoisted_binary)
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_command_binds_script_to_the_installation_node() {
|
||||
let installed = runtime("/runtime/node-24", Some("/runtime/node-24-mcp/dist/index.js"));
|
||||
|
|
|
|||
Loading…
Reference in New Issue