mcp_server_sqlite/tools/
create_fts_index_tool.rs1use rmcp::model::{Content, IntoContents};
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use super::ToolError;
9use crate::{mcp::McpServerSqlite, traits::SqliteServerTool};
10
11#[derive(
12 Clone,
13 Copy,
14 Debug,
15 PartialEq,
16 Eq,
17 PartialOrd,
18 Ord,
19 Hash,
20 Default,
21 Serialize,
22 Deserialize,
23 JsonSchema,
24)]
25pub struct CreateFtsIndexTool;
30
31impl SqliteServerTool for CreateFtsIndexTool {
32 const NAME: &str = "create_fts_index";
33
34 type Context = McpServerSqlite;
35 type Error = ToolError<CreateFtsIndexError>;
36
37 type Input = CreateFtsIndexInput;
38 type Output = CreateFtsIndexOutput;
39
40 fn handle(
41 ctx: &Self::Context,
42 input: Self::Input,
43 ) -> Result<Self::Output, Self::Error> {
44 if input.columns.is_empty() {
45 return Err(ToolError::Tool(CreateFtsIndexError::NoColumns));
46 }
47
48 let conn = ctx
49 .connection()
50 .map_err(|source| ToolError::Connection { source })?;
51
52 let fts_table = input
53 .fts_table_name
54 .unwrap_or_else(|| format!("{}_fts", input.table_name));
55
56 let columns = input.columns.join(", ");
57 let sql = format!(
58 "CREATE VIRTUAL TABLE [{}] USING fts5({}, content=[{}])",
59 fts_table, columns, input.table_name,
60 );
61
62 conn.execute_batch(&sql).map_err(|source| {
63 ToolError::Tool(CreateFtsIndexError::Create { source })
64 })?;
65
66 Ok(CreateFtsIndexOutput {
67 fts_table_name: fts_table,
68 })
69 }
70}
71
72#[derive(
74 Clone,
75 Debug,
76 PartialEq,
77 Eq,
78 PartialOrd,
79 Ord,
80 Hash,
81 Serialize,
82 Deserialize,
83 schemars::JsonSchema,
84)]
85pub struct CreateFtsIndexInput {
86 #[schemars(description = "The source table to create an FTS index over")]
88 pub table_name: String,
89 #[schemars(description = "The columns to include in the full-text index")]
91 pub columns: Vec<String>,
92 #[schemars(
95 description = "Optional name for the FTS virtual table (defaults to {table}_fts)"
96 )]
97 pub fts_table_name: Option<String>,
98}
99
100#[derive(
102 Clone,
103 Debug,
104 PartialEq,
105 Eq,
106 PartialOrd,
107 Ord,
108 Hash,
109 Serialize,
110 Deserialize,
111 schemars::JsonSchema,
112)]
113pub struct CreateFtsIndexOutput {
114 pub fts_table_name: String,
116}
117
118#[derive(Debug, thiserror::Error)]
120pub enum CreateFtsIndexError {
121 #[error("no columns specified for the FTS index")]
123 NoColumns,
124 #[error("failed to create FTS index: {source}")]
126 Create {
127 source: rusqlite::Error,
129 },
130}
131
132impl IntoContents for CreateFtsIndexError {
134 fn into_contents(self) -> Vec<Content> {
135 vec![Content::text(self.to_string())]
136 }
137}