mcp-server-sqlite 1.0.0

An MCP server for SQLite with fine-grained access control
Documentation
//! The `list_tables` tool: returns the names and schemas of all tables in the
//! database. Useful for discovery before issuing queries.

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,
)]
/// List all tables in the database with their names and CREATE TABLE schemas.
/// Returns an empty list if no tables exist. Useful for discovering the
/// database structure before writing queries.
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 })
    }
}

/// The input parameters for the `list_tables` tool.
#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Serialize,
    Deserialize,
    schemars::JsonSchema,
)]
pub struct ListTablesInput {}

/// The result of listing tables in the database.
#[derive(
    Clone,
    Debug,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Serialize,
    Deserialize,
    schemars::JsonSchema,
)]
pub struct ListTablesOutput {
    /// The tables found in the database, each with its name and creation SQL.
    pub tables: Vec<TableInfo>,
}

/// Metadata for a single table in the database.
#[derive(
    Clone,
    Debug,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Serialize,
    Deserialize,
    schemars::JsonSchema,
)]
pub struct TableInfo {
    /// The table name.
    pub name: String,
    /// The `CREATE TABLE` SQL statement that defines this table's schema.
    /// `None` for internal SQLite tables that have no user-visible DDL.
    pub sql: Option<String>,
}

/// Errors specific to the `list_tables` tool.
#[derive(Debug, thiserror::Error)]
pub enum ListTablesError {
    /// Failed to query `sqlite_master` for the table list.
    #[error("failed to list tables: {source}")]
    Query {
        /// The underlying rusqlite error.
        source: rusqlite::Error,
    },
}

/// Converts the list-tables-specific error into MCP content by rendering the
/// display string as text.
impl IntoContents for ListTablesError {
    fn into_contents(self) -> Vec<Content> {
        vec![Content::text(self.to_string())]
    }
}