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