use rmcp::model::{Content, IntoContents};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::ToolError;
use crate::{mcp::McpServerSqlite, traits::SqliteServerTool};
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Default,
Serialize,
Deserialize,
JsonSchema,
)]
pub struct CreateFtsIndexTool;
impl SqliteServerTool for CreateFtsIndexTool {
const NAME: &str = "create_fts_index";
type Context = McpServerSqlite;
type Error = ToolError<CreateFtsIndexError>;
type Input = CreateFtsIndexInput;
type Output = CreateFtsIndexOutput;
fn handle(
ctx: &Self::Context,
input: Self::Input,
) -> Result<Self::Output, Self::Error> {
if input.columns.is_empty() {
return Err(ToolError::Tool(CreateFtsIndexError::NoColumns));
}
let conn = ctx
.connection()
.map_err(|source| ToolError::Connection { source })?;
let fts_table = input
.fts_table_name
.unwrap_or_else(|| format!("{}_fts", input.table_name));
let columns = input.columns.join(", ");
let sql = format!(
"CREATE VIRTUAL TABLE [{}] USING fts5({}, content=[{}])",
fts_table, columns, input.table_name,
);
conn.execute_batch(&sql).map_err(|source| {
ToolError::Tool(CreateFtsIndexError::Create { source })
})?;
Ok(CreateFtsIndexOutput {
fts_table_name: fts_table,
})
}
}
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct CreateFtsIndexInput {
#[schemars(description = "The source table to create an FTS index over")]
pub table_name: String,
#[schemars(description = "The columns to include in the full-text index")]
pub columns: Vec<String>,
#[schemars(
description = "Optional name for the FTS virtual table (defaults to {table}_fts)"
)]
pub fts_table_name: Option<String>,
}
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct CreateFtsIndexOutput {
pub fts_table_name: String,
}
#[derive(Debug, thiserror::Error)]
pub enum CreateFtsIndexError {
#[error("no columns specified for the FTS index")]
NoColumns,
#[error("failed to create FTS index: {source}")]
Create {
source: rusqlite::Error,
},
}
impl IntoContents for CreateFtsIndexError {
fn into_contents(self) -> Vec<Content> {
vec![Content::text(self.to_string())]
}
}