Skip to content

Commit 30b1d08

Browse files
committed
surfaces command error for mcp servers in non-interactive mode
1 parent 43325b1 commit 30b1d08

File tree

2 files changed

+60
-20
lines changed

2 files changed

+60
-20
lines changed

crates/chat-cli/src/cli/chat/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ use token_counter::{
107107
use tokio::signal::ctrl_c;
108108
use tool_manager::{
109109
GetPromptError,
110+
LoadingRecord,
110111
McpServerConfig,
111112
PromptBundle,
112113
ToolManager,
@@ -2961,6 +2962,15 @@ impl ChatContext {
29612962
.collect::<Vec<_>>()
29622963
.join("");
29632964
for (server_name, msg) in loaded_servers.iter() {
2965+
let msg = msg
2966+
.iter()
2967+
.map(|record| match record {
2968+
LoadingRecord::Err(content)
2969+
| LoadingRecord::Warn(content)
2970+
| LoadingRecord::Success(content) => content.clone(),
2971+
})
2972+
.collect::<Vec<_>>()
2973+
.join("\n--- tools refreshed ---\n");
29642974
queue!(
29652975
self.output,
29662976
style::Print(server_name),

crates/chat-cli/src/cli/chat/tool_manager.rs

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@ enum LoadingMsg {
159159
Terminate { still_loading: Vec<String> },
160160
}
161161

162+
/// Used to denote the loading outcome associated with a server.
163+
/// This is mainly used in the non-interactive mode to determine if there is any fatal errors to
164+
/// surface (since we would only want to surface fatal errors in non-interactive mode).
165+
#[derive(Clone, Debug)]
166+
pub enum LoadingRecord {
167+
Success(String),
168+
Warn(String),
169+
Err(String),
170+
}
171+
162172
// This is to mirror claude's config set up
163173
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
164174
#[serde(rename_all = "camelCase")]
@@ -399,7 +409,7 @@ impl ToolManagerBuilder {
399409
let notify = Arc::new(Notify::new());
400410
let notify_weak = Arc::downgrade(&notify);
401411
let completed_clone = completed.clone();
402-
let load_record = Arc::new(Mutex::new(HashMap::<String, String>::new()));
412+
let load_record = Arc::new(Mutex::new(HashMap::<String, Vec<LoadingRecord>>::new()));
403413
let load_record_clone = load_record.clone();
404414
tokio::spawn(async move {
405415
let mut record_temp_buf = Vec::<u8>::new();
@@ -463,10 +473,10 @@ impl ToolManagerBuilder {
463473
has_new_stuff_clone.store(true, Ordering::Release);
464474
// Maintain a record of the server load:
465475
let mut buf_writer = BufWriter::new(&mut record_temp_buf);
466-
if let Err(e) = process_result {
476+
if let Err(e) = &process_result {
467477
let _ = queue_warn_message(
468478
server_name.as_str(),
469-
&e,
479+
e,
470480
time_taken.as_str(),
471481
&mut buf_writer,
472482
);
@@ -480,32 +490,38 @@ impl ToolManagerBuilder {
480490
let _ = buf_writer.flush();
481491
drop(buf_writer);
482492
let record = String::from_utf8_lossy(&record_temp_buf).to_string();
493+
let record = if process_result.is_err() {
494+
LoadingRecord::Warn(record)
495+
} else {
496+
LoadingRecord::Success(record)
497+
};
483498
load_record_clone
484499
.lock()
485500
.await
486501
.entry(server_name)
487502
.and_modify(|load_record| {
488-
load_record.push_str("\n--- tools refreshed ---\n");
489-
load_record.push_str(record.as_str());
503+
load_record.push(record.clone());
490504
})
491-
.or_insert(record);
505+
.or_insert(vec![record]);
492506
},
493507
Err(e) => {
508+
// Log error to chat Log
509+
error!("Error loading server {server_name}: {:?}", e);
494510
// Maintain a record of the server load:
495511
let mut buf_writer = BufWriter::new(&mut record_temp_buf);
496512
let _ = queue_failure_message(server_name.as_str(), &e, &time_taken, &mut buf_writer);
497513
let _ = buf_writer.flush();
498514
drop(buf_writer);
499515
let record = String::from_utf8_lossy(&record_temp_buf).to_string();
516+
let record = LoadingRecord::Err(record);
500517
load_record_clone
501518
.lock()
502519
.await
503520
.entry(server_name.clone())
504521
.and_modify(|load_record| {
505-
load_record.push_str("\n--- tools refreshed ---\n");
506-
load_record.push_str(record.as_str());
522+
load_record.push(record.clone());
507523
})
508-
.or_insert(record);
524+
.or_insert(vec![record]);
509525
// Errors surfaced at this point (i.e. before [process_tool_specs]
510526
// is called) are fatals and should be considered errors
511527
if let Some(sender) = &loading_status_sender_clone {
@@ -762,7 +778,7 @@ pub struct ToolManager {
762778
/// (which may be different than how it is written in the config, depending of the presence of
763779
/// invalid characters).
764780
/// The value is the load message (i.e. load time, warnings, and errors)
765-
pub mcp_load_record: Arc<Mutex<HashMap<String, String>>>,
781+
pub mcp_load_record: Arc<Mutex<HashMap<String, Vec<LoadingRecord>>>>,
766782
}
767783

768784
impl Clone for ToolManager {
@@ -829,16 +845,7 @@ impl ToolManager {
829845
.settings
830846
.get_int(Setting::McpNoInteractiveTimeout)
831847
.map_or(30_000_u64, |s| s as u64);
832-
Box::pin(async move {
833-
tokio::time::sleep(std::time::Duration::from_millis(init_timeout)).await;
834-
let _ = queue!(
835-
output,
836-
style::Print(
837-
"Not all mcp servers loaded. Configure no-interactive timeout with q settings mcp.noInteractiveTimeout"
838-
),
839-
style::Print("\n")
840-
);
841-
})
848+
Box::pin(tokio::time::sleep(std::time::Duration::from_millis(init_timeout)))
842849
};
843850
let server_loading_fut: Pin<Box<dyn Future<Output = ()>>> = if let Some(notify) = notify {
844851
Box::pin(async move { notify.notified().await })
@@ -851,6 +858,13 @@ impl ToolManager {
851858
let still_loading = self.pending_clients.read().await.iter().cloned().collect::<Vec<_>>();
852859
let _ = tx.send(LoadingMsg::Terminate { still_loading }).await;
853860
}
861+
let _ = queue!(
862+
output,
863+
style::Print(
864+
"Not all mcp servers loaded. Configure no-interactive timeout with q settings mcp.noInteractiveTimeout"
865+
),
866+
style::Print("\n------\n")
867+
);
854868
},
855869
_ = server_loading_fut => {
856870
if let Some(tx) = tx {
@@ -869,6 +883,22 @@ impl ToolManager {
869883
}
870884
}
871885
}
886+
if !self.is_interactive
887+
&& self
888+
.mcp_load_record
889+
.lock()
890+
.await
891+
.iter()
892+
.any(|(_, records)| records.iter().any(|record| matches!(record, LoadingRecord::Err(_))))
893+
{
894+
queue!(
895+
output,
896+
style::Print(
897+
"One or more mcp server did not load correctly. See $TMPDIR/qlog/chat.log for more details."
898+
),
899+
style::Print("\n------\n")
900+
)?;
901+
}
872902
self.update().await;
873903
Ok(self.schema.clone())
874904
}

0 commit comments

Comments
 (0)