dbmcp_postgres/tools/
list_views.rs1use std::borrow::Cow;
4
5use dbmcp_server::pagination::Pager;
6use dbmcp_server::types::{ListViewsRequest, ListViewsResponse};
7use dbmcp_sql::Connection as _;
8use dbmcp_sql::sanitize::validate_ident;
9use rmcp::handler::server::router::tool::{AsyncTool, ToolBase};
10use rmcp::model::{ErrorData, ToolAnnotations};
11
12use crate::PostgresHandler;
13
14pub(crate) struct ListViewsTool;
16
17impl ListViewsTool {
18 const NAME: &'static str = "listViews";
19 const TITLE: &'static str = "List Views";
20 const DESCRIPTION: &'static str = r#"List all views in a database.
21
22<usecase>
23Use when:
24- Exploring a database to find defined views alongside tables
25- Verifying a view exists before querying it
26- The user asks what views are in a database
27</usecase>
28
29<examples>
30✓ "What views are in the mydb database?" → listViews(database="mydb")
31✓ "Does an active_users view exist?" → listViews to check
32✗ "Show me the columns of a view" → use getTableSchema instead
33✗ "List materialized views" → use listMaterializedViews
34</examples>
35
36<what_it_returns>
37A sorted JSON array of view name strings in the `public` schema.
38</what_it_returns>
39
40<pagination>
41Paginated. Pass the prior response's `nextCursor` as `cursor` to fetch the next page.
42</pagination>"#;
43}
44
45impl ToolBase for ListViewsTool {
46 type Parameter = ListViewsRequest;
47 type Output = ListViewsResponse;
48 type Error = ErrorData;
49
50 fn name() -> Cow<'static, str> {
51 Self::NAME.into()
52 }
53
54 fn title() -> Option<String> {
55 Some(Self::TITLE.into())
56 }
57
58 fn description() -> Option<Cow<'static, str>> {
59 Some(Self::DESCRIPTION.into())
60 }
61
62 fn annotations() -> Option<ToolAnnotations> {
63 Some(
64 ToolAnnotations::new()
65 .read_only(true)
66 .destructive(false)
67 .idempotent(true)
68 .open_world(false),
69 )
70 }
71}
72
73impl AsyncTool<PostgresHandler> for ListViewsTool {
74 async fn invoke(handler: &PostgresHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
75 handler.list_views(params).await
76 }
77}
78
79impl PostgresHandler {
80 pub async fn list_views(
88 &self,
89 ListViewsRequest { database, cursor }: ListViewsRequest,
90 ) -> Result<ListViewsResponse, ErrorData> {
91 let database = database
92 .as_deref()
93 .map(str::trim)
94 .filter(|s| !s.is_empty())
95 .map(validate_ident)
96 .transpose()?;
97
98 let pager = Pager::new(cursor, self.config.page_size);
99 let query = format!(
100 r"
101 SELECT viewname
102 FROM pg_views
103 WHERE schemaname = 'public'
104 ORDER BY viewname
105 LIMIT {} OFFSET {}",
106 pager.limit(),
107 pager.offset(),
108 );
109
110 let rows: Vec<String> = self.connection.fetch_scalar(query.as_str(), database).await?;
111 let (views, next_cursor) = pager.finalize(rows);
112
113 Ok(ListViewsResponse { views, next_cursor })
114 }
115}