mcp_server_sqlite/tools/
backup_tool.rs1use 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)]
27pub 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#[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 #[schemars(description = "The file path to write the backup to")]
88 pub destination: PathBuf,
89}
90
91#[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 pub pages_copied: i32,
107 pub destination: PathBuf,
109}
110
111#[derive(Debug, thiserror::Error)]
113pub enum BackupError {
114 #[error("failed to back up database: {source}")]
118 Backup {
119 source: rusqlite::Error,
121 },
122}
123
124impl IntoContents for BackupError {
127 fn into_contents(self) -> Vec<Content> {
128 vec![Content::text(self.to_string())]
129 }
130}