Skip to main content

mixtape_tools/sqlite/
mod.rs

1//! SQLite database management tools
2//!
3//! This module provides a comprehensive set of tools for managing SQLite databases
4//! through an AI agent. Tools are separated into read-only (safe) and write/modify
5//! (destructive) categories for granular permission control.
6//!
7//! # Quick Start
8//!
9//! Use the helper functions to add tool groups to your agent:
10//!
11//! ```rust,ignore
12//! use mixtape_core::Agent;
13//! use mixtape_tools::sqlite;
14//!
15//! // Read-only agent - can explore but not modify databases
16//! let agent = Agent::builder()
17//!     .bedrock(ClaudeSonnet4)
18//!     .add_tools(sqlite::read_only_tools())
19//!     .build()
20//!     .await?;
21//!
22//! // Full access agent - can read, write, and manage schemas
23//! let agent = Agent::builder()
24//!     .bedrock(ClaudeSonnet4)
25//!     .add_tools(sqlite::all_tools())
26//!     .build()
27//!     .await?;
28//! ```
29//!
30//! # Tool Groups
31//!
32//! | Function | Tools | Use Case |
33//! |----------|-------|----------|
34//! | [`read_only_tools()`] | 9 tools | Database exploration, querying, backups |
35//! | [`mutative_tools()`] | 4 tools | Data modifications |
36//! | [`transaction_tools()`] | 3 tools | Transaction management |
37//! | [`migration_tools()`] | 7 tools | Schema evolution via stored migrations |
38//! | [`all_tools()`] | 23 tools | Full database management |
39//!
40//! # Common Patterns
41//!
42//! ## Read-Only Database Explorer
43//!
44//! For agents that should only query and explore databases without modifying them:
45//!
46//! ```rust,ignore
47//! use mixtape_tools::sqlite;
48//!
49//! let agent = Agent::builder()
50//!     .add_tools(sqlite::read_only_tools())
51//!     .build()
52//!     .await?;
53//! ```
54//!
55//! This includes: open/close/list databases, list/describe tables, SELECT queries,
56//! schema export, and backups.
57//!
58//! ## Data Entry Agent
59//!
60//! For agents that need to insert/update data but not modify schema:
61//!
62//! ```rust,ignore
63//! use mixtape_tools::sqlite::{self, *};
64//!
65//! let agent = Agent::builder()
66//!     .add_tools(sqlite::read_only_tools())
67//!     .add_tool(WriteQueryTool)      // INSERT/UPDATE/DELETE
68//!     .add_tool(BulkInsertTool)      // Batch inserts
69//!     .add_tools(sqlite::transaction_tools())
70//!     .build()
71//!     .await?;
72//! ```
73//!
74//! ## Schema Migration Agent
75//!
76//! For agents that manage database schemas via migrations:
77//!
78//! ```rust,ignore
79//! use mixtape_tools::sqlite;
80//!
81//! let agent = Agent::builder()
82//!     .add_tools(sqlite::read_only_tools())
83//!     .add_tools(sqlite::migration_tools())
84//!     .build()
85//!     .await?;
86//! ```
87//!
88//! # Tool Categories
89//!
90//! ## Database Management (Safe)
91//! - `sqlite_open_database` - Open or create a database
92//! - `sqlite_close_database` - Close a database connection
93//! - `sqlite_list_databases` - Discover database files in a directory
94//! - `sqlite_database_info` - Get database metadata and statistics
95//!
96//! ## Table Operations
97//! - `sqlite_list_tables` - List all tables and views (Safe)
98//! - `sqlite_describe_table` - Get table schema details (Safe)
99//!
100//! ## Query Operations
101//! - `sqlite_read_query` - Execute SELECT/PRAGMA/EXPLAIN queries (Safe)
102//! - `sqlite_write_query` - Execute INSERT/UPDATE/DELETE queries (Destructive)
103//! - `sqlite_schema_query` - Execute DDL statements (Destructive)
104//! - `sqlite_bulk_insert` - Batch insert records (Destructive)
105//!
106//! ## Transaction Management (Configurable)
107//! - `sqlite_begin_transaction` - Start a transaction
108//! - `sqlite_commit_transaction` - Commit a transaction
109//! - `sqlite_rollback_transaction` - Rollback a transaction
110//!
111//! ## Maintenance Operations
112//! - `sqlite_export_schema` - Export schema as SQL or JSON (Safe)
113//! - `sqlite_backup` - Create a database backup (Safe)
114//! - `sqlite_vacuum` - Optimize database storage (Destructive)
115//!
116//! ## Migration Operations
117//! - `sqlite_add_migration` - Store a new pending migration (Destructive)
118//! - `sqlite_run_migrations` - Apply pending migrations in order (Destructive)
119//! - `sqlite_list_migrations` - List migrations with status filter (Safe)
120//! - `sqlite_get_migration` - Get migration details by version (Safe)
121//! - `sqlite_remove_migration` - Remove a pending migration (Destructive)
122//! - `sqlite_export_migrations` - Export migrations for transfer (Safe)
123//! - `sqlite_import_migrations` - Import migrations as pending (Destructive)
124
125pub mod database;
126pub mod error;
127pub mod maintenance;
128pub mod manager;
129pub mod migration;
130pub mod query;
131pub mod table;
132#[cfg(test)]
133pub mod test_utils;
134pub mod transaction;
135pub mod types;
136
137// Re-export commonly used items
138pub use database::{CloseDatabaseTool, DatabaseInfoTool, ListDatabasesTool, OpenDatabaseTool};
139pub use error::SqliteToolError;
140pub use maintenance::{BackupDatabaseTool, ExportSchemaTool, VacuumDatabaseTool};
141pub use manager::{with_connection, DATABASE_MANAGER};
142pub use migration::{
143    AddMigrationTool, ExportMigrationsTool, GetMigrationTool, ImportMigrationsTool,
144    ListMigrationsTool, RemoveMigrationTool, RunMigrationsTool,
145};
146pub use query::{BulkInsertTool, ReadQueryTool, SchemaQueryTool, WriteQueryTool};
147pub use table::{DescribeTableTool, ListTablesTool};
148pub use transaction::{BeginTransactionTool, CommitTransactionTool, RollbackTransactionTool};
149pub use types::*;
150
151use mixtape_core::tool::{box_tool, DynTool};
152
153/// Returns all read-only SQLite tools
154///
155/// These tools cannot modify data or schema - only query and export.
156pub fn read_only_tools() -> Vec<Box<dyn DynTool>> {
157    vec![
158        box_tool(OpenDatabaseTool),
159        box_tool(CloseDatabaseTool),
160        box_tool(ListDatabasesTool),
161        box_tool(DatabaseInfoTool),
162        box_tool(ListTablesTool),
163        box_tool(DescribeTableTool),
164        box_tool(ReadQueryTool),
165        box_tool(ExportSchemaTool),
166        box_tool(BackupDatabaseTool),
167    ]
168}
169
170/// Returns all mutative (write/modify) SQLite tools
171pub fn mutative_tools() -> Vec<Box<dyn DynTool>> {
172    vec![
173        box_tool(WriteQueryTool),
174        box_tool(SchemaQueryTool),
175        box_tool(BulkInsertTool),
176        box_tool(VacuumDatabaseTool),
177    ]
178}
179
180/// Returns all transaction management SQLite tools
181pub fn transaction_tools() -> Vec<Box<dyn DynTool>> {
182    vec![
183        box_tool(BeginTransactionTool),
184        box_tool(CommitTransactionTool),
185        box_tool(RollbackTransactionTool),
186    ]
187}
188
189/// Returns all migration management SQLite tools
190///
191/// These tools allow agents to evolve database schemas over time by storing
192/// and executing migrations within the database itself.
193pub fn migration_tools() -> Vec<Box<dyn DynTool>> {
194    vec![
195        box_tool(AddMigrationTool),
196        box_tool(RunMigrationsTool),
197        box_tool(ListMigrationsTool),
198        box_tool(GetMigrationTool),
199        box_tool(RemoveMigrationTool),
200        box_tool(ExportMigrationsTool),
201        box_tool(ImportMigrationsTool),
202    ]
203}
204
205/// Returns all SQLite tools
206pub fn all_tools() -> Vec<Box<dyn DynTool>> {
207    let mut tools = read_only_tools();
208    tools.extend(mutative_tools());
209    tools.extend(transaction_tools());
210    tools.extend(migration_tools());
211    tools
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    fn test_read_only_tools_count_and_names() {
220        let tools = read_only_tools();
221        assert_eq!(tools.len(), 9);
222
223        let names: Vec<&str> = tools.iter().map(|t| t.name()).collect();
224        assert!(names.contains(&"sqlite_open_database"));
225        assert!(names.contains(&"sqlite_close_database"));
226        assert!(names.contains(&"sqlite_list_databases"));
227        assert!(names.contains(&"sqlite_database_info"));
228        assert!(names.contains(&"sqlite_list_tables"));
229        assert!(names.contains(&"sqlite_describe_table"));
230        assert!(names.contains(&"sqlite_read_query"));
231        assert!(names.contains(&"sqlite_export_schema"));
232        assert!(names.contains(&"sqlite_backup"));
233    }
234
235    #[test]
236    fn test_mutative_tools_count_and_names() {
237        let tools = mutative_tools();
238        assert_eq!(tools.len(), 4);
239
240        let names: Vec<&str> = tools.iter().map(|t| t.name()).collect();
241        assert!(names.contains(&"sqlite_write_query"));
242        assert!(names.contains(&"sqlite_schema_query"));
243        assert!(names.contains(&"sqlite_bulk_insert"));
244        assert!(names.contains(&"sqlite_vacuum"));
245    }
246
247    #[test]
248    fn test_transaction_tools_count_and_names() {
249        let tools = transaction_tools();
250        assert_eq!(tools.len(), 3);
251
252        let names: Vec<&str> = tools.iter().map(|t| t.name()).collect();
253        assert!(names.contains(&"sqlite_begin_transaction"));
254        assert!(names.contains(&"sqlite_commit_transaction"));
255        assert!(names.contains(&"sqlite_rollback_transaction"));
256    }
257
258    #[test]
259    fn test_migration_tools_count_and_names() {
260        let tools = migration_tools();
261        assert_eq!(tools.len(), 7);
262
263        let names: Vec<&str> = tools.iter().map(|t| t.name()).collect();
264        assert!(names.contains(&"sqlite_add_migration"));
265        assert!(names.contains(&"sqlite_run_migrations"));
266        assert!(names.contains(&"sqlite_list_migrations"));
267        assert!(names.contains(&"sqlite_get_migration"));
268        assert!(names.contains(&"sqlite_remove_migration"));
269        assert!(names.contains(&"sqlite_export_migrations"));
270        assert!(names.contains(&"sqlite_import_migrations"));
271    }
272
273    #[test]
274    fn test_all_tools_combines_categories() {
275        let all = all_tools();
276        let read_only = read_only_tools();
277        let mutative = mutative_tools();
278        let transaction = transaction_tools();
279        let migration = migration_tools();
280
281        assert_eq!(
282            all.len(),
283            read_only.len() + mutative.len() + transaction.len() + migration.len()
284        );
285        assert_eq!(all.len(), 23);
286    }
287
288    #[test]
289    fn test_tools_have_descriptions() {
290        for tool in all_tools() {
291            assert!(
292                !tool.description().is_empty(),
293                "Tool {} has empty description",
294                tool.name()
295            );
296        }
297    }
298
299    #[test]
300    fn test_tools_have_schemas() {
301        for tool in all_tools() {
302            let schema = tool.input_schema();
303            assert!(
304                schema.is_object(),
305                "Tool {} schema is not an object",
306                tool.name()
307            );
308        }
309    }
310
311    #[test]
312    fn test_no_duplicate_tool_names() {
313        let tools = all_tools();
314        let names: Vec<&str> = tools.iter().map(|t| t.name()).collect();
315        let mut unique_names = names.clone();
316        unique_names.sort();
317        unique_names.dedup();
318        assert_eq!(
319            names.len(),
320            unique_names.len(),
321            "Duplicate tool names found"
322        );
323    }
324}