Skip to main content

dbmcp_sqlite/
types.rs

1//! SQLite-specific MCP tool request types.
2//!
3//! Unlike `MySQL` and `PostgreSQL`, `SQLite` operates on a single file and
4//! has no database selection, so these types omit the `database` field
5//! present in the shared server types. `ListEntries` and
6//! `ListTablesResponse` live in the shared `dbmcp-server` crate; they are
7//! re-exported here so call sites can keep importing them from
8//! `crate::types`.
9
10use dbmcp_server::pagination::Cursor;
11use schemars::JsonSchema;
12use serde::Deserialize;
13
14pub use dbmcp_server::types::{ListEntries, ListTablesResponse};
15
16/// Request for the `dropTable` tool.
17#[derive(Debug, Default, Deserialize, JsonSchema)]
18pub struct DropTableRequest {
19    /// Name of the table to drop. Must be non-empty.
20    pub table: String,
21}
22
23/// Request for the `SQLite` `listTables` tool — supports optional search filter and detailed mode.
24#[derive(Debug, Default, Deserialize, JsonSchema)]
25pub struct ListTablesRequest {
26    /// Opaque pagination cursor. Omit (or pass `null`) for the first page.
27    /// On subsequent calls, pass the `nextCursor` returned by the previous
28    /// response verbatim. Cursors are opaque — do not parse, modify, or persist.
29    #[serde(default)]
30    pub cursor: Option<Cursor>,
31    /// Optional case-insensitive filter on table names. The input is used within a `LIKE`
32    /// clause: `%` matches any sequence of characters and `_` matches any single character.
33    #[serde(default)]
34    pub search: Option<String>,
35    /// When `true`, each returned entry is a full metadata object (columns,
36    /// constraints, indexes, triggers); when `false` or omitted, each entry
37    /// is the bare table-name string.
38    #[serde(default)]
39    pub detailed: bool,
40}
41
42/// Request for the `listViews` tool.
43#[derive(Debug, Default, Deserialize, JsonSchema)]
44pub struct ListViewsRequest {
45    /// Opaque pagination cursor. Omit (or pass `null`) for the first page.
46    /// On subsequent calls, pass the `nextCursor` returned by the previous
47    /// response verbatim. Cursors are opaque — do not parse, modify, or persist.
48    #[serde(default)]
49    pub cursor: Option<Cursor>,
50}
51
52/// Request for the `SQLite` `listTriggers` tool — supports optional search filter and detailed mode.
53#[derive(Debug, Default, Deserialize, JsonSchema)]
54pub struct ListTriggersRequest {
55    /// Opaque pagination cursor. Omit (or pass `null`) for the first page.
56    /// On subsequent calls, pass the `nextCursor` returned by the previous
57    /// response verbatim. Cursors are opaque — do not parse, modify, or persist.
58    #[serde(default)]
59    pub cursor: Option<Cursor>,
60    /// Optional case-insensitive filter on trigger names. The input is used within a `LIKE`
61    /// clause: `%` matches any sequence of characters and `_` matches any single character.
62    #[serde(default)]
63    pub search: Option<String>,
64    /// When `true`, each returned entry is a full metadata object (schema,
65    /// table, definition); when `false` or omitted, each entry is the bare
66    /// trigger-name string.
67    #[serde(default)]
68    pub detailed: bool,
69}
70
71/// Request for the `writeQuery` tool.
72#[derive(Debug, Default, Deserialize, JsonSchema)]
73pub struct QueryRequest {
74    /// The SQL query to execute.
75    pub query: String,
76}
77
78/// Request for the `readQuery` tool.
79#[derive(Debug, Default, Deserialize, JsonSchema)]
80pub struct ReadQueryRequest {
81    /// The SQL query to execute.
82    pub query: String,
83    /// Opaque pagination cursor. Omit (or pass `null`) for the first page.
84    /// On subsequent calls, pass the `nextCursor` returned by the previous
85    /// response verbatim. Cursors are opaque — do not parse, modify, or persist.
86    /// Ignored for `EXPLAIN` statements.
87    #[serde(default)]
88    pub cursor: Option<Cursor>,
89}
90
91/// Request for the `explainQuery` tool.
92#[derive(Debug, Default, Deserialize, JsonSchema)]
93pub struct ExplainQueryRequest {
94    /// The SQL query to explain.
95    pub query: String,
96}
97
98#[cfg(test)]
99mod tests {
100    use super::{ListTablesRequest, ListTriggersRequest};
101
102    #[test]
103    fn list_tables_request_defaults_to_brief_mode_without_search() {
104        let req: ListTablesRequest = serde_json::from_str("{}").expect("empty object should parse");
105        assert!(req.search.is_none());
106        assert!(!req.detailed, "detailed must default to false");
107    }
108
109    #[test]
110    fn list_tables_request_accepts_search_and_detailed() {
111        let req: ListTablesRequest = serde_json::from_str(r#"{"search": "post", "detailed": true}"#).expect("parse");
112        assert_eq!(req.search.as_deref(), Some("post"));
113        assert!(req.detailed);
114    }
115
116    #[test]
117    fn list_triggers_request_defaults_to_brief_mode_without_search() {
118        let req: ListTriggersRequest = serde_json::from_str("{}").expect("empty object should parse");
119        assert!(req.search.is_none());
120        assert!(!req.detailed, "detailed must default to false");
121    }
122
123    #[test]
124    fn list_triggers_request_accepts_search_and_detailed() {
125        let req: ListTriggersRequest = serde_json::from_str(r#"{"search": "audit", "detailed": true}"#).expect("parse");
126        assert_eq!(req.search.as_deref(), Some("audit"));
127        assert!(req.detailed);
128    }
129}