database_mcp_postgres/tools/
list_databases.rs1use std::borrow::Cow;
4
5use database_mcp_server::pagination::Pager;
6use database_mcp_server::types::{ListDatabasesRequest, ListDatabasesResponse};
7use database_mcp_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 = r#"List all accessible databases on the connected server. Use this tool to discover what databases are available before using other tools.
20
21<usecase>
22ALWAYS call this tool FIRST when:
23- You need to explore what databases exist on the server
24- You need a database name for listTables, getTableSchema, or query tools
25- The user asks what data is available
26</usecase>
27
28<examples>
29✓ "What databases are on this server?"
30✓ "Show me what's available" → call listDatabases first
31</examples>
32
33<what_it_returns>
34A sorted JSON array of database name strings.
35</what_it_returns>
36
37<pagination>
38Paginated. Pass the prior response's `nextCursor` as `cursor` to fetch the next page.
39</pagination>"#;
40}
41
42impl ToolBase for ListDatabasesTool {
43 type Parameter = ListDatabasesRequest;
44 type Output = ListDatabasesResponse;
45 type Error = ErrorData;
46
47 fn name() -> Cow<'static, str> {
48 Self::NAME.into()
49 }
50
51 fn title() -> Option<String> {
52 Some(Self::TITLE.into())
53 }
54
55 fn description() -> Option<Cow<'static, str>> {
56 Some(Self::DESCRIPTION.into())
57 }
58
59 fn annotations() -> Option<ToolAnnotations> {
60 Some(
61 ToolAnnotations::new()
62 .read_only(true)
63 .destructive(false)
64 .idempotent(true)
65 .open_world(false),
66 )
67 }
68}
69
70impl AsyncTool<PostgresHandler> for ListDatabasesTool {
71 async fn invoke(handler: &PostgresHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
72 handler.list_databases(params).await
73 }
74}
75
76impl PostgresHandler {
77 pub async fn list_databases(
88 &self,
89 ListDatabasesRequest { cursor }: ListDatabasesRequest,
90 ) -> Result<ListDatabasesResponse, ErrorData> {
91 let pager = Pager::new(cursor, self.config.page_size);
92 let query = format!(
93 r"
94 SELECT datname
95 FROM pg_database
96 WHERE datistemplate = false
97 ORDER BY datname
98 LIMIT {} OFFSET {}",
99 pager.limit(),
100 pager.offset(),
101 );
102
103 let rows: Vec<String> = self.connection.fetch_scalar(query.as_str(), None).await?;
104 let (databases, next_cursor) = pager.finalize(rows);
105
106 Ok(ListDatabasesResponse { databases, next_cursor })
107 }
108}