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}