database_mcp_mysql/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::MysqlHandler;
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<MysqlHandler> for ListDatabasesTool {
71 async fn invoke(handler: &MysqlHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
72 handler.list_databases(params).await
73 }
74}
75
76impl MysqlHandler {
77 pub async fn list_databases(
84 &self,
85 ListDatabasesRequest { cursor }: ListDatabasesRequest,
86 ) -> Result<ListDatabasesResponse, ErrorData> {
87 let pager = Pager::new(cursor, self.config.page_size);
88 let query = format!(
89 r"
90 SELECT CAST(SCHEMA_NAME AS CHAR)
91 FROM information_schema.SCHEMATA
92 ORDER BY SCHEMA_NAME
93 LIMIT {} OFFSET {}",
94 pager.limit(),
95 pager.offset(),
96 );
97
98 let rows: Vec<String> = self.connection.fetch_scalar(query.as_str(), None).await?;
99 let (databases, next_cursor) = pager.finalize(rows);
100
101 Ok(ListDatabasesResponse { databases, next_cursor })
102 }
103}