dbmcp_postgres/tools/
list_databases.rs1use std::borrow::Cow;
4
5use dbmcp_server::pagination::Pager;
6use dbmcp_server::types::{ListDatabasesRequest, ListDatabasesResponse};
7use dbmcp_sql::Connection as _;
8use rmcp::handler::server::router::tool::{AsyncTool, ToolBase};
9use rmcp::model::{ErrorData, ToolAnnotations};
10
11use crate::PostgresHandler;
12
13const NAME: &str = "listDatabases";
14const TITLE: &str = "List Databases";
15const DESCRIPTION: &str = include_str!("../../assets/tools/list_databases/default.md");
16
17pub(crate) struct ListDatabasesTool;
19
20impl ToolBase for ListDatabasesTool {
21 type Parameter = ListDatabasesRequest;
22 type Output = ListDatabasesResponse;
23 type Error = ErrorData;
24
25 fn name() -> Cow<'static, str> {
26 NAME.into()
27 }
28
29 fn title() -> Option<String> {
30 Some(TITLE.into())
31 }
32
33 fn description() -> Option<Cow<'static, str>> {
34 Some(DESCRIPTION.into())
35 }
36
37 fn annotations() -> Option<ToolAnnotations> {
38 Some(
39 ToolAnnotations::new()
40 .read_only(true)
41 .destructive(false)
42 .idempotent(true)
43 .open_world(false),
44 )
45 }
46}
47
48impl AsyncTool<PostgresHandler> for ListDatabasesTool {
49 async fn invoke(handler: &PostgresHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
50 handler.list_databases(params).await
51 }
52}
53
54impl PostgresHandler {
55 pub async fn list_databases(
66 &self,
67 ListDatabasesRequest { cursor }: ListDatabasesRequest,
68 ) -> Result<ListDatabasesResponse, ErrorData> {
69 let pager = Pager::new(cursor, self.config.page_size);
70 let query = format!(
71 r"
72 SELECT datname
73 FROM pg_database
74 WHERE datistemplate = false
75 ORDER BY datname
76 LIMIT {} OFFSET {}",
77 pager.limit(),
78 pager.offset(),
79 );
80
81 let rows: Vec<String> = self.connection.fetch_scalar(query.as_str(), None).await?;
82 let (databases, next_cursor) = pager.paginate(rows);
83
84 Ok(ListDatabasesResponse { databases, next_cursor })
85 }
86}