@@ -33,6 +33,7 @@ impl McpServerHandle {
3333struct AppState {
3434 buffer : ReceiveBuffer ,
3535 serial : SerialState ,
36+ esp : crate :: esp_cmd:: EspHandle ,
3637 app_handle : tauri:: AppHandle ,
3738}
3839
@@ -41,6 +42,7 @@ impl Clone for AppState {
4142 Self {
4243 buffer : self . buffer . clone ( ) ,
4344 serial : self . serial . clone ( ) ,
45+ esp : self . esp . clone ( ) ,
4446 app_handle : self . app_handle . clone ( ) ,
4547 }
4648 }
@@ -180,6 +182,30 @@ async fn handle_tools_list(id: Value) -> (StatusCode, Json<Value>) {
180182 "type" : "object" ,
181183 "properties" : { }
182184 }
185+ } ,
186+ {
187+ "name" : "esp_build_flash" ,
188+ "description" : "对 ESP-IDF 项目执行 build + flash(等同于主界面的火焰按钮),完成后芯片自动复位运行" ,
189+ "inputSchema" : {
190+ "type" : "object" ,
191+ "properties" : {
192+ "project_dir" : {
193+ "type" : "string" ,
194+ "description" : "ESP-IDF 项目根目录(含 CMakeLists.txt)"
195+ } ,
196+ "port" : {
197+ "type" : "string" ,
198+ "description" : "串口, 缺省用当前选中的串口" ,
199+ "default" : ""
200+ } ,
201+ "baud" : {
202+ "type" : "integer" ,
203+ "description" : "烧录波特率, 缺省用设置里的值" ,
204+ "default" : 0
205+ }
206+ } ,
207+ "required" : [ "project_dir" ]
208+ }
183209 }
184210 ]
185211 } ) ) ;
@@ -256,6 +282,82 @@ async fn handle_tools_call(state: &AppState, body: &Value, id: Value) -> (Status
256282 } ) ) ;
257283 ( StatusCode :: OK , Json ( resp) )
258284 }
285+ "esp_build_flash" => {
286+ let project_dir = args. get ( "project_dir" )
287+ . and_then ( |v| v. as_str ( ) )
288+ . unwrap_or ( "" )
289+ . to_string ( ) ;
290+ let port = args. get ( "port" )
291+ . and_then ( |v| v. as_str ( ) )
292+ . unwrap_or ( "" )
293+ . to_string ( ) ;
294+ let baud = args. get ( "baud" ) . and_then ( |v| v. as_u64 ( ) ) . map ( |v| v as u32 ) ;
295+
296+ if project_dir. is_empty ( ) {
297+ let resp = jsonrpc_error ( -32602 , "project_dir cannot be empty" ) ;
298+ return ( StatusCode :: BAD_REQUEST , Json ( resp) ) ;
299+ }
300+
301+ let cfg = {
302+ let c = state. esp . config . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
303+ crate :: esp_cmd:: EspConfig {
304+ idf_path : c. idf_path . clone ( ) ,
305+ python_path : c. python_path . clone ( ) ,
306+ baud : baud. unwrap_or ( c. baud ) ,
307+ }
308+ } ;
309+ if cfg. idf_path . trim ( ) . is_empty ( ) {
310+ let resp = jsonrpc_error ( -32603 , "ESP-IDF 路径未配置, 请先在系统设置里填写" ) ;
311+ return ( StatusCode :: INTERNAL_SERVER_ERROR , Json ( resp) ) ;
312+ }
313+
314+ {
315+ let mut busy = state. esp . busy . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
316+ if * busy {
317+ let resp = jsonrpc_error ( -32603 , "已有烧录任务在运行" ) ;
318+ return ( StatusCode :: INTERNAL_SERVER_ERROR , Json ( resp) ) ;
319+ }
320+ * busy = true ;
321+ }
322+
323+ let esp = state. esp . clone ( ) ;
324+ let app_handle = state. app_handle . clone ( ) ;
325+ let serial_name = state
326+ . serial
327+ . port_name
328+ . lock ( )
329+ . unwrap_or_else ( |e| e. into_inner ( ) )
330+ . clone ( )
331+ . unwrap_or_default ( ) ;
332+ let port = if port. is_empty ( ) { serial_name } else { port } ;
333+ let result = tokio:: task:: spawn_blocking ( move || {
334+ let r = crate :: esp_cmd:: run_worker ( & esp, & app_handle, & cfg, & project_dir, & port) ;
335+ let mut busy = esp. busy . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
336+ * busy = false ;
337+ r
338+ } )
339+ . await ;
340+
341+ match result {
342+ Ok ( Ok ( ( ) ) ) => {
343+ let resp = jsonrpc_success ( id, json ! ( {
344+ "content" : [ {
345+ "type" : "text" ,
346+ "text" : "build + flash 成功, 芯片已复位运行"
347+ } ]
348+ } ) ) ;
349+ ( StatusCode :: OK , Json ( resp) )
350+ }
351+ Ok ( Err ( e) ) => {
352+ let resp = jsonrpc_error ( -32603 , & format ! ( "烧录失败: {}" , e) ) ;
353+ ( StatusCode :: INTERNAL_SERVER_ERROR , Json ( resp) )
354+ }
355+ Err ( e) => {
356+ let resp = jsonrpc_error ( -32603 , & format ! ( "任务异常: {}" , e) ) ;
357+ ( StatusCode :: INTERNAL_SERVER_ERROR , Json ( resp) )
358+ }
359+ }
360+ }
259361 _ => {
260362 let resp = jsonrpc_error ( -32602 , & format ! ( "Tool not found: {}" , tool_name) ) ;
261363 ( StatusCode :: NOT_FOUND , Json ( resp) )
@@ -296,6 +398,7 @@ pub async fn mcp_start(
296398 handle : tauri:: State < ' _ , McpServerHandle > ,
297399 buffer : tauri:: State < ' _ , ReceiveBuffer > ,
298400 serial : tauri:: State < ' _ , SerialState > ,
401+ esp : tauri:: State < ' _ , crate :: esp_cmd:: EspHandle > ,
299402 port : u16 ,
300403) -> Result < ( ) , String > {
301404 let mut running = handle. running . lock ( ) . await ;
@@ -307,6 +410,7 @@ pub async fn mcp_start(
307410 let state = AppState {
308411 buffer : ( * buffer) . clone ( ) ,
309412 serial : ( * serial) . clone ( ) ,
413+ esp : ( * esp) . clone ( ) ,
310414 app_handle : app_handle. clone ( ) ,
311415 } ;
312416
0 commit comments