From a529344ac7b2dbe21a6e00c9d6b774f57d0db2e7 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Wed, 1 Jul 2026 15:14:01 +0800 Subject: [PATCH] fix(mcp): write bridge port to resolved data dir --- src-tauri/src/commands/mcp_bridge.rs | 43 +++++++++++++++++++++++++--- src-tauri/src/lib.rs | 2 +- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/commands/mcp_bridge.rs b/src-tauri/src/commands/mcp_bridge.rs index 9be12c473..dcc131643 100644 --- a/src-tauri/src/commands/mcp_bridge.rs +++ b/src-tauri/src/commands/mcp_bridge.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; use std::sync::Arc; -use tauri::{AppHandle, Emitter, Manager}; +use tauri::{AppHandle, Emitter}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpListener; @@ -9,6 +10,7 @@ use super::connection::AppState; use super::connection::ensure_connection_writable; const BIND_ADDR: &str = "127.0.0.1:0"; +const MCP_BRIDGE_PORT_FILE: &str = "mcp-bridge-port"; #[derive(Deserialize)] struct OpenTableRequest { @@ -131,7 +133,7 @@ pub struct McpExecuteQueryEvent { pub allow_dangerous: bool, } -pub fn start(app_handle: AppHandle, state: Arc) { +pub fn start(app_handle: AppHandle, state: Arc, data_dir: PathBuf) { tauri::async_runtime::spawn(async move { let listener = match TcpListener::bind(BIND_ADDR).await { Ok(l) => l, @@ -143,8 +145,9 @@ pub fn start(app_handle: AppHandle, state: Arc) { log::info!("MCP bridge listening on {BIND_ADDR}"); let actual_port = listener.local_addr().map(|a| a.port()).unwrap_or(0); log::info!("MCP bridge assigned port {actual_port}"); - if let Ok(dir) = app_handle.path().app_data_dir() { - let _ = std::fs::write(dir.join("mcp-bridge-port"), actual_port.to_string()); + // Publish into DBX's resolved data dir so DBX_DATA_DIR and portable mode share the same discovery file. + if let Err(err) = write_port_file(&data_dir, actual_port) { + log::warn!("MCP bridge failed to write port file in {}: {err}", data_dir.display()); } loop { let (mut stream, _) = match listener.accept().await { @@ -202,6 +205,13 @@ pub fn start(app_handle: AppHandle, state: Arc) { }); } +fn write_port_file(data_dir: &Path, actual_port: u16) -> std::io::Result { + std::fs::create_dir_all(data_dir)?; + let path = data_dir.join(MCP_BRIDGE_PORT_FILE); + std::fs::write(&path, actual_port.to_string())?; + Ok(path) +} + fn find_config_by_name<'a>( configs: &'a [crate::models::connection::ConnectionConfig], name: &str, @@ -209,6 +219,31 @@ fn find_config_by_name<'a>( configs.iter().find(|c| c.name.eq_ignore_ascii_case(name)) } +#[cfg(test)] +mod tests { + use super::write_port_file; + + #[test] + fn writes_bridge_port_file_to_resolved_data_dir() { + let root = std::env::temp_dir().join(format!( + "dbx-mcp-bridge-port-test-{}-{}", + std::process::id(), + uuid::Uuid::new_v4() + )); + let default_data_dir = root.join("default-app-data"); + let resolved_data_dir = root.join("resolved-data"); + std::fs::create_dir_all(&default_data_dir).unwrap(); + + let port_file = write_port_file(&resolved_data_dir, 49152).unwrap(); + + assert_eq!(port_file, resolved_data_dir.join("mcp-bridge-port")); + assert_eq!(std::fs::read_to_string(port_file).unwrap(), "49152"); + assert!(!default_data_dir.join("mcp-bridge-port").exists()); + + let _ = std::fs::remove_dir_all(root); + } +} + async fn respond(stream: &mut tokio::net::TcpStream, status: &str, body: &str) { let resp = format!("HTTP/1.1 {status}\r\nContent-Length: {}\r\n\r\n{body}", body.len()); let _ = stream.write_all(resp.as_bytes()).await; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 475729068..3dd1f42e7 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -582,7 +582,7 @@ pub fn run() { open_connection_deep_links(app.handle(), startup_links); let app_handle = app.handle().clone(); - commands::mcp_bridge::start(app_handle, state); + commands::mcp_bridge::start(app_handle, state, data_dir); eprintln!("[STARTUP] setup complete in {:?} (total {:?})", setup_start.elapsed(), startup_begin.elapsed()); if let Some(decorations) = native_window_decorations_override(std::env::consts::OS) {