Skip to main content

mcp_server_sqlite/tools/
backup_tool.rs

1//! The `backup` tool: creates a full backup of the database to a file path
2//! using rusqlite's online backup API.
3
4use std::path::PathBuf;
5
6use rmcp::model::{Content, IntoContents};
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10use super::ToolError;
11use crate::{mcp::McpServerSqlite, traits::SqliteServerTool};
12
13#[derive(
14    Clone,
15    Copy,
16    Debug,
17    PartialEq,
18    Eq,
19    PartialOrd,
20    Ord,
21    Hash,
22    Default,
23    Serialize,
24    Deserialize,
25    JsonSchema,
26)]
27/// Back up the entire database to a file. Creates a consistent snapshot of all
28/// tables, indexes, triggers, and views at the destination path. The backup
29/// runs incrementally in pages so it does not block other operations for long.
30pub struct BackupTool;
31
32impl SqliteServerTool for BackupTool {
33    const NAME: &str = "backup";
34
35    type Context = McpServerSqlite;
36    type Error = ToolError<BackupError>;
37
38    type Input = BackupInput;
39    type Output = BackupOutput;
40
41    fn handle(
42        ctx: &Self::Context,
43        input: Self::Input,
44    ) -> Result<Self::Output, Self::Error> {
45        let conn = ctx
46            .connection()
47            .map_err(|source| ToolError::Connection { source })?;
48
49        let mut dst = rusqlite::Connection::open(&input.destination).map_err(
50            |source| ToolError::Tool(BackupError::Backup { source }),
51        )?;
52
53        let backup = rusqlite::backup::Backup::new(&conn, &mut dst).map_err(
54            |source| ToolError::Tool(BackupError::Backup { source }),
55        )?;
56
57        backup
58            .run_to_completion(5, std::time::Duration::from_millis(50), None)
59            .map_err(|source| {
60                ToolError::Tool(BackupError::Backup { source })
61            })?;
62
63        let pages_copied = backup.progress().pagecount;
64
65        Ok(BackupOutput {
66            pages_copied,
67            destination: input.destination,
68        })
69    }
70}
71
72/// The input parameters for the `backup` tool.
73#[derive(
74    Clone,
75    Debug,
76    PartialEq,
77    Eq,
78    PartialOrd,
79    Ord,
80    Hash,
81    Serialize,
82    Deserialize,
83    schemars::JsonSchema,
84)]
85pub struct BackupInput {
86    /// The file path where the backup will be written.
87    #[schemars(description = "The file path to write the backup to")]
88    pub destination: PathBuf,
89}
90
91/// The result of a successful database backup.
92#[derive(
93    Clone,
94    Debug,
95    PartialEq,
96    Eq,
97    PartialOrd,
98    Ord,
99    Hash,
100    Serialize,
101    Deserialize,
102    schemars::JsonSchema,
103)]
104pub struct BackupOutput {
105    /// The total number of pages copied during the backup.
106    pub pages_copied: i32,
107    /// The file path the backup was written to.
108    pub destination: PathBuf,
109}
110
111/// Errors specific to the `backup` tool.
112#[derive(Debug, thiserror::Error)]
113pub enum BackupError {
114    /// The backup operation failed. This may indicate the destination path is
115    /// not writable, the disk is full, or an internal SQLite error occurred
116    /// during the copy.
117    #[error("failed to back up database: {source}")]
118    Backup {
119        /// The underlying rusqlite error.
120        source: rusqlite::Error,
121    },
122}
123
124/// Converts the backup-specific error into MCP content by rendering the display
125/// string as text.
126impl IntoContents for BackupError {
127    fn into_contents(self) -> Vec<Content> {
128        vec![Content::text(self.to_string())]
129    }
130}