use std::path::PathBuf;
use rmcp::model::{Content, IntoContents};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::ToolError;
use crate::{mcp::McpServerSqlite, traits::SqliteServerTool};
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Default,
Serialize,
Deserialize,
JsonSchema,
)]
pub struct BackupTool;
impl SqliteServerTool for BackupTool {
const NAME: &str = "backup";
type Context = McpServerSqlite;
type Error = ToolError<BackupError>;
type Input = BackupInput;
type Output = BackupOutput;
fn handle(
ctx: &Self::Context,
input: Self::Input,
) -> Result<Self::Output, Self::Error> {
let conn = ctx
.connection()
.map_err(|source| ToolError::Connection { source })?;
let mut dst = rusqlite::Connection::open(&input.destination).map_err(
|source| ToolError::Tool(BackupError::Backup { source }),
)?;
let backup = rusqlite::backup::Backup::new(&conn, &mut dst).map_err(
|source| ToolError::Tool(BackupError::Backup { source }),
)?;
backup
.run_to_completion(5, std::time::Duration::from_millis(50), None)
.map_err(|source| {
ToolError::Tool(BackupError::Backup { source })
})?;
let pages_copied = backup.progress().pagecount;
Ok(BackupOutput {
pages_copied,
destination: input.destination,
})
}
}
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct BackupInput {
#[schemars(description = "The file path to write the backup to")]
pub destination: PathBuf,
}
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct BackupOutput {
pub pages_copied: i32,
pub destination: PathBuf,
}
#[derive(Debug, thiserror::Error)]
pub enum BackupError {
#[error("failed to back up database: {source}")]
Backup {
source: rusqlite::Error,
},
}
impl IntoContents for BackupError {
fn into_contents(self) -> Vec<Content> {
vec![Content::text(self.to_string())]
}
}