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 ListViewsTool;
impl SqliteServerTool for ListViewsTool {
const NAME: &str = "list_views";
type Context = McpServerSqlite;
type Error = ToolError<ListViewsError>;
type Input = ListViewsInput;
type Output = ListViewsOutput;
fn handle(
ctx: &Self::Context,
_input: Self::Input,
) -> Result<Self::Output, Self::Error> {
let conn = ctx
.connection()
.map_err(|source| ToolError::Connection { source })?;
let mut stmt = conn
.prepare(
"SELECT name, sql FROM sqlite_master \
WHERE type = 'view' ORDER BY name",
)
.map_err(|source| {
ToolError::Tool(ListViewsError::Query { source })
})?;
let views = stmt
.query_map([], |row| {
Ok(ViewInfo {
name: row.get(0)?,
sql: row.get(1)?,
})
})
.map_err(|source| {
ToolError::Tool(ListViewsError::Query { source })
})?
.collect::<Result<Vec<_>, _>>()
.map_err(|source| {
ToolError::Tool(ListViewsError::Query { source })
})?;
Ok(ListViewsOutput { views })
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct ListViewsInput {}
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct ListViewsOutput {
pub views: Vec<ViewInfo>,
}
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct ViewInfo {
pub name: String,
pub sql: String,
}
#[derive(Debug, thiserror::Error)]
pub enum ListViewsError {
#[error("failed to list views: {source}")]
Query {
source: rusqlite::Error,
},
}
impl IntoContents for ListViewsError {
fn into_contents(self) -> Vec<Content> {
vec![Content::text(self.to_string())]
}
}