@@ -4,7 +4,11 @@ use serde_json::{Map, Value};
44use std:: fs;
55use std:: { collections:: HashMap , env, sync:: Arc , time:: Duration } ;
66use tauri:: { AppHandle , Emitter , Runtime , State } ;
7- use tokio:: { process:: Command , sync:: Mutex , time:: { timeout, sleep} } ;
7+ use tokio:: {
8+ process:: Command ,
9+ sync:: Mutex ,
10+ time:: { sleep, timeout} ,
11+ } ;
812
913use super :: { cmd:: get_jan_data_folder_path, state:: AppState } ;
1014
@@ -13,9 +17,6 @@ const DEFAULT_MCP_CONFIG: &str = r#"{"mcpServers":{"browsermcp":{"command":"npx"
1317// Timeout for MCP tool calls (30 seconds)
1418const MCP_TOOL_CALL_TIMEOUT : Duration = Duration :: from_secs ( 30 ) ;
1519
16- // Maximum retry attempts for MCP server loading
17- const MAX_MCP_RETRY_ATTEMPTS : u32 = 3 ;
18-
1920// Base backoff duration in milliseconds (exponential backoff will multiply this)
2021const BASE_BACKOFF_MS : u64 = 1000 ;
2122
@@ -52,15 +53,19 @@ pub async fn run_mcp_commands<R: Runtime>(
5253 log:: trace!( "Server {name} is not active, skipping." ) ;
5354 continue ;
5455 }
55-
56+
5657 // Try to start the server with retry mechanism
57- start_mcp_server (
58+ if let Err ( e ) = start_mcp_server (
5859 app. clone ( ) ,
5960 servers_state. clone ( ) ,
6061 name. clone ( ) ,
6162 config. clone ( ) ,
63+ None , // Use default max retry attempts (0)
6264 )
63- . await ;
65+ . await
66+ {
67+ log:: error!( "Failed to start MCP server {name}: {e}" ) ;
68+ }
6469 }
6570 }
6671
@@ -73,79 +78,94 @@ async fn start_mcp_server<R: Runtime>(
7378 servers_state : Arc < Mutex < HashMap < String , RunningService < RoleClient , ( ) > > > > ,
7479 name : String ,
7580 config : Value ,
76- ) {
81+ max_retry_attempts : Option < u32 > ,
82+ ) -> Result < ( ) , String > {
83+ // Use default value of 0 if None is provided
84+ let max_retry_attempts = max_retry_attempts. unwrap_or ( 0 ) ;
85+
7786 // Initialize retry state locally for this server start attempt
7887 let mut retry_count = 0 ;
7988 let mut backoff_ms = BASE_BACKOFF_MS ;
80-
89+
8190 loop {
8291 retry_count += 1 ;
83-
92+
8493 if retry_count > 1 {
8594 log:: info!(
8695 "Starting MCP server {name} - retry attempt {} of {} (waiting {:.1}s)" ,
8796 retry_count,
88- MAX_MCP_RETRY_ATTEMPTS ,
97+ max_retry_attempts ,
8998 backoff_ms as f64 / 1000.0
9099 ) ;
91-
100+
92101 let _ = app. emit (
93102 "mcp-retry-attempt" ,
94103 serde_json:: json!( {
95104 "server" : name,
96105 "attempt" : retry_count,
97- "max_attempts" : MAX_MCP_RETRY_ATTEMPTS
98- } )
106+ "max_attempts" : max_retry_attempts
107+ } ) ,
99108 ) ;
100-
109+
101110 sleep ( Duration :: from_millis ( backoff_ms) ) . await ;
102111 } else {
103112 log:: info!( "Starting MCP server {name} - initial attempt" ) ;
104113 }
105-
114+
106115 // Attempt to start the server
107- match schedule_mcp_start_task ( app. clone ( ) , servers_state. clone ( ) , name. clone ( ) , config. clone ( ) ) . await {
116+ match schedule_mcp_start_task (
117+ app. clone ( ) ,
118+ servers_state. clone ( ) ,
119+ name. clone ( ) ,
120+ config. clone ( ) ,
121+ )
122+ . await
123+ {
108124 Ok ( _) => {
109125 log:: info!( "Server {name} activated successfully." ) ;
110-
126+
111127 let _ = app. emit (
112128 "mcp-server-started" ,
113129 serde_json:: json!( {
114130 "server" : name,
115131 "status" : "success" ,
116132 "attempts" : retry_count
117- } )
133+ } ) ,
118134 ) ;
119- return ;
135+ return Ok ( ( ) ) ;
120136 }
121137 Err ( e) => {
122138 log:: error!( "Failed to activate server {name} (attempt {retry_count}): {e}" ) ;
123-
124- if retry_count >= MAX_MCP_RETRY_ATTEMPTS {
139+
140+ // Check if we've exceeded the maximum retry attempts
141+ // retry_count starts at 1 for the first attempt, so retry_count - 1 is the number of retries done
142+ if retry_count > max_retry_attempts {
125143 log:: error!(
126- "Server {name} has exceeded maximum retry attempts ({}). Giving up." ,
127- MAX_MCP_RETRY_ATTEMPTS
144+ "Server {name} has exceeded maximum retry attempts ({max_retry_attempts}). Giving up."
128145 ) ;
129146 let _ = app. emit (
130147 "mcp-max-retries-exceeded" ,
131- format ! ( "MCP server {name} failed after {MAX_MCP_RETRY_ATTEMPTS} attempts: {e}" ) ,
132- ) ;
133- return ;
134- } else {
135- let _ = app. emit (
136- "mcp-retry-scheduled" ,
137- serde_json:: json!( {
138- "server" : name,
139- "attempt" : retry_count,
140- "max_attempts" : MAX_MCP_RETRY_ATTEMPTS ,
141- "error" : e,
142- "next_retry_in_ms" : backoff_ms
143- } )
148+ format ! (
149+ "MCP server {name} failed after {retry_count} attempts ({max_retry_attempts} retries): {e}"
150+ ) ,
144151 ) ;
145-
146- // Update backoff duration for next attempt (exponential backoff)
147- backoff_ms = ( backoff_ms * 2 ) . min ( 30000 ) ; // Cap at 30 seconds
152+ return Err ( format ! (
153+ "MCP server {name} failed after {retry_count} attempts ({max_retry_attempts} retries): {e}"
154+ ) ) ;
148155 }
156+ let _ = app. emit (
157+ "mcp-retry-scheduled" ,
158+ serde_json:: json!( {
159+ "server" : name,
160+ "attempt" : retry_count,
161+ "max_attempts" : max_retry_attempts,
162+ "error" : e,
163+ "next_retry_in_ms" : backoff_ms
164+ } ) ,
165+ ) ;
166+
167+ // Update backoff duration for next attempt (exponential backoff)
168+ backoff_ms = ( backoff_ms * 2 ) . min ( 30000 ) ; // Cap at 30 seconds
149169 }
150170 }
151171 }
@@ -160,8 +180,7 @@ pub async fn activate_mcp_server<R: Runtime>(
160180) -> Result < ( ) , String > {
161181 let servers: Arc < Mutex < HashMap < String , RunningService < RoleClient , ( ) > > > > =
162182 state. mcp_servers . clone ( ) ;
163- start_mcp_server ( app, servers, name, config) . await ;
164- Ok ( ( ) )
183+ start_mcp_server ( app, servers, name, config, None ) . await
165184}
166185
167186async fn schedule_mcp_start_task < R : Runtime > (
@@ -196,7 +215,8 @@ async fn schedule_mcp_start_task<R: Runtime>(
196215 cmd. arg ( "run" ) ;
197216 cmd. env ( "UV_CACHE_DIR" , cache_dir. to_str ( ) . unwrap ( ) . to_string ( ) ) ;
198217 }
199- #[ cfg( windows) ] {
218+ #[ cfg( windows) ]
219+ {
200220 cmd. creation_flags ( 0x08000000 ) ; // CREATE_NO_WINDOW: prevents shell window on Windows
201221 }
202222 let app_path_str = app_path. to_str ( ) . unwrap ( ) . to_string ( ) ;
0 commit comments