database_mcp_postgres/tools/
list_tables.rs1use std::borrow::Cow;
4
5use database_mcp_server::AppError;
6use database_mcp_server::types::{ListTablesRequest, ListTablesResponse};
7use database_mcp_sql::timeout::execute_with_timeout;
8use rmcp::handler::server::router::tool::{AsyncTool, ToolBase};
9use rmcp::model::{ErrorData, ToolAnnotations};
10
11use crate::PostgresHandler;
12
13pub(crate) struct ListTablesTool;
15
16impl ListTablesTool {
17 const NAME: &'static str = "list_tables";
18 const DESCRIPTION: &'static str =
19 "List all tables in a specific database.\nRequires `database_name` from `list_databases`.";
20}
21
22impl ToolBase for ListTablesTool {
23 type Parameter = ListTablesRequest;
24 type Output = ListTablesResponse;
25 type Error = ErrorData;
26
27 fn name() -> Cow<'static, str> {
28 Self::NAME.into()
29 }
30
31 fn description() -> Option<Cow<'static, str>> {
32 Some(Self::DESCRIPTION.into())
33 }
34
35 fn annotations() -> Option<ToolAnnotations> {
36 Some(
37 ToolAnnotations::new()
38 .read_only(true)
39 .destructive(false)
40 .idempotent(true)
41 .open_world(false),
42 )
43 }
44}
45
46impl AsyncTool<PostgresHandler> for ListTablesTool {
47 async fn invoke(handler: &PostgresHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
48 Ok(handler.list_tables(¶ms).await?)
49 }
50}
51
52impl PostgresHandler {
53 pub async fn list_tables(&self, request: &ListTablesRequest) -> Result<ListTablesResponse, AppError> {
59 let db = if request.database_name.is_empty() {
60 None
61 } else {
62 Some(request.database_name.as_str())
63 };
64 let pool = self.get_pool(db).await?;
65 let sql = "SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename";
66 let rows: Vec<(String,)> =
67 execute_with_timeout(self.config.query_timeout, sql, sqlx::query_as(sql).fetch_all(&pool)).await?;
68 Ok(ListTablesResponse {
69 tables: rows.into_iter().map(|r| r.0).collect(),
70 })
71 }
72}