Skip to main content

mcp_server_sqlite/tools/
database_info_tool.rs

1//! The `database_info` tool: returns metadata about the SQLite database
2//! including file size, page metrics, journal mode, WAL status, version, and
3//! object counts.
4
5use rmcp::model::{Content, IntoContents};
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8
9use super::ToolError;
10use crate::{mcp::McpServerSqlite, traits::SqliteServerTool};
11
12#[derive(
13    Clone,
14    Copy,
15    Debug,
16    PartialEq,
17    Eq,
18    PartialOrd,
19    Ord,
20    Hash,
21    Default,
22    Serialize,
23    Deserialize,
24    JsonSchema,
25)]
26/// Return metadata about the database: SQLite version, page size, page count,
27/// journal mode, WAL checkpoint status, total size in bytes, freelist (unused)
28/// page count, and the number of tables and indexes. Useful for monitoring
29/// database health and understanding storage characteristics.
30pub struct DatabaseInfoTool;
31
32impl SqliteServerTool for DatabaseInfoTool {
33    const NAME: &str = "database_info";
34
35    type Context = McpServerSqlite;
36    type Error = ToolError<DatabaseInfoError>;
37
38    type Input = DatabaseInfoInput;
39    type Output = DatabaseInfoOutput;
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 query_err =
50            |source| ToolError::Tool(DatabaseInfoError::Query { source });
51
52        let sqlite_version: String = conn
53            .query_row("SELECT sqlite_version()", [], |row| row.get(0))
54            .map_err(query_err)?;
55
56        let page_size: i64 = conn
57            .query_row("PRAGMA page_size", [], |row| row.get(0))
58            .map_err(query_err)?;
59
60        let page_count: i64 = conn
61            .query_row("PRAGMA page_count", [], |row| row.get(0))
62            .map_err(query_err)?;
63
64        let journal_mode: String = conn
65            .query_row("PRAGMA journal_mode", [], |row| row.get(0))
66            .map_err(query_err)?;
67
68        let wal_checkpoint = if journal_mode == "wal" {
69            let info = conn
70                .query_row("PRAGMA wal_checkpoint(PASSIVE)", [], |row| {
71                    Ok(WalCheckpointInfo {
72                        busy: row.get(0)?,
73                        log_pages: row.get(1)?,
74                        checkpointed_pages: row.get(2)?,
75                    })
76                })
77                .map_err(query_err)?;
78            Some(info)
79        } else {
80            None
81        };
82
83        let database_size_bytes = page_size * page_count;
84
85        let freelist_count: i64 = conn
86            .query_row("PRAGMA freelist_count", [], |row| row.get(0))
87            .map_err(query_err)?;
88
89        let table_count: i64 = conn
90            .query_row(
91                "SELECT COUNT(*) FROM sqlite_master \
92                 WHERE type = 'table'",
93                [],
94                |row| row.get(0),
95            )
96            .map_err(query_err)?;
97
98        let index_count: i64 = conn
99            .query_row(
100                "SELECT COUNT(*) FROM sqlite_master \
101                 WHERE type = 'index'",
102                [],
103                |row| row.get(0),
104            )
105            .map_err(query_err)?;
106
107        Ok(DatabaseInfoOutput {
108            sqlite_version,
109            page_size,
110            page_count,
111            journal_mode,
112            wal_checkpoint,
113            database_size_bytes,
114            freelist_count,
115            table_count,
116            index_count,
117        })
118    }
119}
120
121/// The input parameters for the `database_info` tool.
122#[derive(
123    Clone,
124    Copy,
125    Debug,
126    Default,
127    PartialEq,
128    Eq,
129    PartialOrd,
130    Ord,
131    Hash,
132    Serialize,
133    Deserialize,
134    schemars::JsonSchema,
135)]
136pub struct DatabaseInfoInput {}
137
138/// The result of querying database metadata.
139#[derive(
140    Clone,
141    Debug,
142    PartialEq,
143    Eq,
144    PartialOrd,
145    Ord,
146    Hash,
147    Serialize,
148    Deserialize,
149    schemars::JsonSchema,
150)]
151pub struct DatabaseInfoOutput {
152    /// The SQLite library version string (e.g. `"3.45.1"`).
153    pub sqlite_version: String,
154    /// The database page size in bytes, as configured by `PRAGMA page_size`.
155    pub page_size: i64,
156    /// The total number of pages in the database file, as reported by `PRAGMA
157    /// page_count`.
158    pub page_count: i64,
159    /// The active journal mode (e.g. `"delete"`, `"wal"`, `"memory"`).
160    pub journal_mode: String,
161    /// WAL checkpoint information, present only when the journal mode is
162    /// `"wal"`. Contains the result of `PRAGMA wal_checkpoint(PASSIVE)`.
163    pub wal_checkpoint: Option<WalCheckpointInfo>,
164    /// The total database size in bytes, computed as `page_size * page_count`.
165    pub database_size_bytes: i64,
166    /// The number of unused pages on the freelist, as reported by `PRAGMA
167    /// freelist_count`.
168    pub freelist_count: i64,
169    /// The number of tables in the database, counted from `sqlite_master`.
170    pub table_count: i64,
171    /// The number of indexes in the database, counted from `sqlite_master`.
172    pub index_count: i64,
173}
174
175/// Information from a passive WAL checkpoint, as returned by `PRAGMA
176/// wal_checkpoint(PASSIVE)`.
177#[derive(
178    Clone,
179    Copy,
180    Debug,
181    PartialEq,
182    Eq,
183    PartialOrd,
184    Ord,
185    Hash,
186    Serialize,
187    Deserialize,
188    schemars::JsonSchema,
189)]
190pub struct WalCheckpointInfo {
191    /// Non-zero if the checkpoint was blocked by a concurrent reader or writer.
192    pub busy: i64,
193    /// The number of pages in the WAL log file.
194    pub log_pages: i64,
195    /// The number of pages successfully checkpointed back to the main database
196    /// file.
197    pub checkpointed_pages: i64,
198}
199
200/// Errors specific to the `database_info` tool.
201#[derive(Debug, thiserror::Error)]
202pub enum DatabaseInfoError {
203    /// A query for database metadata failed.
204    #[error("failed to retrieve database info: {source}")]
205    Query {
206        /// The underlying rusqlite error.
207        source: rusqlite::Error,
208    },
209}
210
211/// Converts the database-info-specific error into MCP content by rendering the
212/// display string as text.
213impl IntoContents for DatabaseInfoError {
214    fn into_contents(self) -> Vec<Content> {
215        vec![Content::text(self.to_string())]
216    }
217}