Skip to main content

dbmcp_sqlite/tools/
explain_query.rs

1//! MCP tool: `explainQuery`.
2
3use dbmcp_pii::MaybeRedact as _;
4use dbmcp_server::types::QueryResponse;
5
6use super::prelude::*;
7use crate::types::ExplainQueryRequest;
8
9const NAME: &str = "explainQuery";
10const TITLE: &str = "Explain Query";
11const DESCRIPTION: &str = include_str!("../../assets/tools/explain_query.md");
12
13/// Marker type for the `explainQuery` MCP tool.
14pub(crate) struct ExplainQueryTool;
15
16impl ToolBase for ExplainQueryTool {
17    type Parameter = ExplainQueryRequest;
18    type Output = QueryResponse;
19    type Error = ErrorData;
20
21    fn name() -> Cow<'static, str> {
22        NAME.into()
23    }
24
25    fn title() -> Option<String> {
26        Some(TITLE.into())
27    }
28
29    fn description() -> Option<Cow<'static, str>> {
30        Some(DESCRIPTION.into())
31    }
32
33    fn annotations() -> Option<ToolAnnotations> {
34        Some(
35            ToolAnnotations::new()
36                .read_only(true)
37                .destructive(false)
38                .idempotent(true)
39                .open_world(true),
40        )
41    }
42
43    fn input_schema() -> Option<Arc<JsonObject>> {
44        Some(input_schema::<Self::Parameter>(true))
45    }
46
47    fn output_schema() -> Option<Arc<JsonObject>> {
48        Some(output_schema::<Self::Output>())
49    }
50}
51
52impl AsyncTool<SqliteHandler> for ExplainQueryTool {
53    async fn invoke(handler: &SqliteHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
54        handler.explain_query(params).await
55    }
56}
57
58impl SqliteHandler {
59    /// Returns the execution plan for a query.
60    ///
61    /// Always uses `EXPLAIN QUERY PLAN` — `SQLite` does not support
62    /// `EXPLAIN ANALYZE`.
63    ///
64    /// # Errors
65    ///
66    /// Returns [`SqlError::Query`] if the backend reports an error.
67    pub async fn explain_query(
68        &self,
69        ExplainQueryRequest { query }: ExplainQueryRequest,
70    ) -> Result<QueryResponse, ErrorData> {
71        let explain_sql = format!("EXPLAIN QUERY PLAN {query}");
72
73        let rows = self.connection.fetch_json(explain_sql.as_str(), None).await?;
74        let rows = self.redactor.redact_rows(rows).await?;
75
76        Ok(QueryResponse { rows })
77    }
78}