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 ListTablesTool;
impl SqliteServerTool for ListTablesTool {
const NAME: &str = "list_tables";
type Context = McpServerSqlite;
type Error = ToolError<ListTablesError>;
type Input = ListTablesInput;
type Output = ListTablesOutput;
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 = 'table' ORDER BY name",
)
.map_err(|source| {
ToolError::Tool(ListTablesError::Query { source })
})?;
let tables = stmt
.query_map([], |row| {
Ok(TableInfo {
name: row.get(0)?,
sql: row.get(1)?,
})
})
.map_err(|source| {
ToolError::Tool(ListTablesError::Query { source })
})?
.collect::<Result<Vec<_>, _>>()
.map_err(|source| {
ToolError::Tool(ListTablesError::Query { source })
})?;
Ok(ListTablesOutput { tables })
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct ListTablesInput {}
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct ListTablesOutput {
pub tables: Vec<TableInfo>,
}
#[derive(
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct TableInfo {
pub name: String,
pub sql: Option<String>,
}
#[derive(Debug, thiserror::Error)]
pub enum ListTablesError {
#[error("failed to list tables: {source}")]
Query {
source: rusqlite::Error,
},
}
impl IntoContents for ListTablesError {
fn into_contents(self) -> Vec<Content> {
vec![Content::text(self.to_string())]
}
}