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}