use std::time::Duration;
use rmcp::{ErrorData as McpError, model::CallToolResult};
use crate::server::handlers::file_edit_common::FileEditFaultInjection;
use crate::server::{ReadFileMode, SshMcpServer};
use crate::tools::{CheckProcessParams, ReadFileParams, ReplaceInFileParams, WriteFileParams};
impl SshMcpServer {
#[doc(hidden)]
pub async fn test_execute_command(
&self,
command: &str,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_command(command).await
}
#[doc(hidden)]
pub async fn test_execute_command_with_timeout_ms(
&self,
command: &str,
timeout_ms: u64,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_command_with_timeout(command, Duration::from_millis(timeout_ms))
.await
}
#[doc(hidden)]
pub async fn test_execute_sudo_command(
&self,
command: &str,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_sudo_command(command).await
}
#[doc(hidden)]
pub async fn test_execute_sudo_command_with_timeout_ms(
&self,
command: &str,
timeout_ms: u64,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_sudo_command_with_timeout(command, Duration::from_millis(timeout_ms))
.await
}
#[doc(hidden)]
pub async fn test_check_process(
&self,
job_id: &str,
tail_lines: usize,
) -> std::result::Result<CallToolResult, McpError> {
let params = CheckProcessParams {
job_id: job_id.to_string(),
tail_lines,
};
self.execute_check_process(params).await
}
#[doc(hidden)]
pub async fn test_read_file(
&self,
remote_path: &str,
timeout_ms: Option<u64>,
) -> std::result::Result<CallToolResult, McpError> {
self.test_read_file_with_options(remote_path, ReadFileMode::Preview, None, timeout_ms)
.await
}
#[doc(hidden)]
pub async fn test_read_file_with_options(
&self,
remote_path: &str,
mode: ReadFileMode,
lines: Option<usize>,
timeout_ms: Option<u64>,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_read_file(ReadFileParams {
remote_path: remote_path.to_string(),
mode,
lines,
timeout_ms,
})
.await
}
#[doc(hidden)]
pub async fn test_write_file(
&self,
remote_path: &str,
new_content: &str,
expected_sha256: Option<&str>,
read_ticket: Option<&str>,
timeout_ms: Option<u64>,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_write_file(
WriteFileParams {
remote_path: remote_path.to_string(),
new_content: new_content.to_string(),
expected_sha256: expected_sha256.map(str::to_string),
read_ticket: read_ticket.map(str::to_string),
dry_run: None,
timeout_ms,
},
FileEditFaultInjection::None,
)
.await
}
#[doc(hidden)]
pub async fn test_write_file_with_params(
&self,
params: WriteFileParams,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_write_file(params, FileEditFaultInjection::None)
.await
}
#[doc(hidden)]
pub async fn test_replace_in_file(
&self,
remote_path: &str,
old_text: &str,
new_text: &str,
replace_all: bool,
expected_sha256: Option<&str>,
timeout_ms: Option<u64>,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_replace_in_file(
ReplaceInFileParams {
remote_path: remote_path.to_string(),
old_text: old_text.to_string(),
new_text: new_text.to_string(),
scope_text: None,
replace_all: Some(replace_all),
match_index: None,
dry_run: None,
expected_sha256: expected_sha256.map(str::to_string),
timeout_ms,
},
FileEditFaultInjection::None,
)
.await
}
#[doc(hidden)]
pub async fn test_replace_in_file_with_params(
&self,
params: ReplaceInFileParams,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_replace_in_file(params, FileEditFaultInjection::None)
.await
}
#[doc(hidden)]
pub async fn test_replace_in_file_delete_before_write(
&self,
remote_path: &str,
old_text: &str,
new_text: &str,
replace_all: bool,
expected_sha256: Option<&str>,
timeout_ms: Option<u64>,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_replace_in_file(
ReplaceInFileParams {
remote_path: remote_path.to_string(),
old_text: old_text.to_string(),
new_text: new_text.to_string(),
scope_text: None,
replace_all: Some(replace_all),
match_index: None,
dry_run: None,
expected_sha256: expected_sha256.map(str::to_string),
timeout_ms,
},
FileEditFaultInjection::PartialDeleteBeforeWrite,
)
.await
}
#[doc(hidden)]
pub async fn test_replace_in_file_mutate_before_write(
&self,
remote_path: &str,
old_text: &str,
new_text: &str,
replace_all: bool,
expected_sha256: Option<&str>,
timeout_ms: Option<u64>,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_replace_in_file(
ReplaceInFileParams {
remote_path: remote_path.to_string(),
old_text: old_text.to_string(),
new_text: new_text.to_string(),
scope_text: None,
replace_all: Some(replace_all),
match_index: None,
dry_run: None,
expected_sha256: expected_sha256.map(str::to_string),
timeout_ms,
},
FileEditFaultInjection::PartialMutateBeforeWrite,
)
.await
}
#[doc(hidden)]
pub async fn test_write_file_fail_before_finalize(
&self,
remote_path: &str,
new_content: &str,
expected_sha256: Option<&str>,
read_ticket: Option<&str>,
timeout_ms: Option<u64>,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_write_file(
WriteFileParams {
remote_path: remote_path.to_string(),
new_content: new_content.to_string(),
expected_sha256: expected_sha256.map(str::to_string),
read_ticket: read_ticket.map(str::to_string),
dry_run: None,
timeout_ms,
},
FileEditFaultInjection::FailBeforeFinalize,
)
.await
}
#[doc(hidden)]
pub async fn test_write_file_sha256_unavailable(
&self,
remote_path: &str,
new_content: &str,
expected_sha256: Option<&str>,
read_ticket: Option<&str>,
timeout_ms: Option<u64>,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_write_file(
WriteFileParams {
remote_path: remote_path.to_string(),
new_content: new_content.to_string(),
expected_sha256: expected_sha256.map(str::to_string),
read_ticket: read_ticket.map(str::to_string),
dry_run: None,
timeout_ms,
},
FileEditFaultInjection::Sha256Unavailable,
)
.await
}
#[doc(hidden)]
pub async fn test_execute_background_command(
&self,
command: &str,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_background_command(command, None).await
}
#[doc(hidden)]
pub async fn test_execute_background_sudo_command(
&self,
command: &str,
) -> std::result::Result<CallToolResult, McpError> {
self.execute_background_sudo_command(command, None).await
}
#[doc(hidden)]
pub async fn test_transfer(
&self,
params: crate::transfer::TransferParams,
) -> crate::transfer::TransferResponse {
let timeout = params
.timeout_ms
.map(Duration::from_millis)
.unwrap_or(self.timeout);
let key_path = self.config.key.clone();
if let Err(e) = self.connection.ensure_connected().await {
return crate::transfer::TransferResponse::error(
params,
self.transfer.local_root(),
&e.to_string(),
);
}
use crate::transfer::{TransferRunContext, TransferSshOptions};
self.transfer
.run(
&self.connection,
params,
TransferRunContext {
timeout,
ssh: TransferSshOptions {
host: self.config.host.clone(),
port: self.config.port,
user: self.config.user.clone(),
key_path,
host_key_checking: self.config.strict_host_key_checking,
known_hosts: self.config.known_hosts.clone(),
},
},
)
.await
}
}