fix: ctrl-C interruption in the CLI (#4057)
Co-authored-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
@@ -105,6 +105,42 @@ impl Default for ExtensionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn child_process_client(
|
||||||
|
mut command: Command,
|
||||||
|
timeout: &Option<u64>,
|
||||||
|
) -> ExtensionResult<McpClient> {
|
||||||
|
command.process_group(0);
|
||||||
|
let (transport, mut stderr) = TokioChildProcess::builder(command)
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
let mut stderr = stderr.take().ok_or_else(|| {
|
||||||
|
ExtensionError::SetupError("failed to attach child process stderr".to_owned())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let stderr_task = tokio::spawn(async move {
|
||||||
|
let mut all_stderr = Vec::new();
|
||||||
|
stderr.read_to_end(&mut all_stderr).await?;
|
||||||
|
Ok::<String, std::io::Error>(String::from_utf8_lossy(&all_stderr).into())
|
||||||
|
});
|
||||||
|
|
||||||
|
let client_result = McpClient::connect(
|
||||||
|
transport,
|
||||||
|
Duration::from_secs(timeout.unwrap_or(crate::config::DEFAULT_EXTENSION_TIMEOUT)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match client_result {
|
||||||
|
Ok(client) => Ok(client),
|
||||||
|
Err(error) => {
|
||||||
|
let error_task_out = stderr_task.await?;
|
||||||
|
Err::<McpClient, ExtensionError>(match error_task_out {
|
||||||
|
Ok(stderr_content) => ProcessExit::new(stderr_content, error).into(),
|
||||||
|
Err(e) => e.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ExtensionManager {
|
impl ExtensionManager {
|
||||||
/// Create a new ExtensionManager instance
|
/// Create a new ExtensionManager instance
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@@ -280,38 +316,7 @@ impl ExtensionManager {
|
|||||||
let command = Command::new(cmd).configure(|command| {
|
let command = Command::new(cmd).configure(|command| {
|
||||||
command.args(args).envs(all_envs);
|
command.args(args).envs(all_envs);
|
||||||
});
|
});
|
||||||
let (transport, mut stderr) = TokioChildProcess::builder(command)
|
let client = child_process_client(command, timeout).await?;
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.spawn()?;
|
|
||||||
let mut stderr = stderr
|
|
||||||
.take()
|
|
||||||
.expect("should have a stderr handle because it was requested");
|
|
||||||
|
|
||||||
let stderr_task = tokio::spawn(async move {
|
|
||||||
let mut all_stderr = Vec::new();
|
|
||||||
stderr.read_to_end(&mut all_stderr).await?;
|
|
||||||
Ok::<String, std::io::Error>(String::from_utf8_lossy(&all_stderr).into())
|
|
||||||
});
|
|
||||||
|
|
||||||
let client_result = McpClient::connect(
|
|
||||||
transport,
|
|
||||||
Duration::from_secs(
|
|
||||||
timeout.unwrap_or(crate::config::DEFAULT_EXTENSION_TIMEOUT),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let client = match client_result {
|
|
||||||
Ok(client) => Ok(client),
|
|
||||||
Err(error) => {
|
|
||||||
let error_task_out = stderr_task.await?;
|
|
||||||
Err::<McpClient, ExtensionError>(match error_task_out {
|
|
||||||
Ok(stderr_content) => ProcessExit::new(stderr_content, error).into(),
|
|
||||||
Err(e) => e.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}?;
|
|
||||||
|
|
||||||
Box::new(client)
|
Box::new(client)
|
||||||
}
|
}
|
||||||
ExtensionConfig::Builtin {
|
ExtensionConfig::Builtin {
|
||||||
@@ -326,19 +331,11 @@ impl ExtensionManager {
|
|||||||
.to_str()
|
.to_str()
|
||||||
.expect("should resolve executable to string path")
|
.expect("should resolve executable to string path")
|
||||||
.to_string();
|
.to_string();
|
||||||
|
let command = Command::new(cmd).configure(|command| {
|
||||||
let transport = TokioChildProcess::new(Command::new(cmd).configure(|command| {
|
|
||||||
command.arg("mcp").arg(name);
|
command.arg("mcp").arg(name);
|
||||||
}))?;
|
});
|
||||||
Box::new(
|
let client = child_process_client(command, timeout).await?;
|
||||||
McpClient::connect(
|
Box::new(client)
|
||||||
transport,
|
|
||||||
Duration::from_secs(
|
|
||||||
timeout.unwrap_or(crate::config::DEFAULT_EXTENSION_TIMEOUT),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
ExtensionConfig::InlinePython {
|
ExtensionConfig::InlinePython {
|
||||||
name,
|
name,
|
||||||
@@ -360,21 +357,11 @@ impl ExtensionManager {
|
|||||||
|
|
||||||
command.arg("python").arg(file_path.to_str().unwrap());
|
command.arg("python").arg(file_path.to_str().unwrap());
|
||||||
});
|
});
|
||||||
let transport = TokioChildProcess::new(command)?;
|
|
||||||
|
|
||||||
let client = Box::new(
|
|
||||||
McpClient::connect(
|
|
||||||
transport,
|
|
||||||
Duration::from_secs(
|
|
||||||
timeout.unwrap_or(crate::config::DEFAULT_EXTENSION_TIMEOUT),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
let client = child_process_client(command, timeout).await?;
|
||||||
self.temp_dirs.insert(sanitized_name.clone(), temp_dir);
|
self.temp_dirs.insert(sanitized_name.clone(), temp_dir);
|
||||||
|
|
||||||
client
|
Box::new(client)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user