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