Skip to main content

database_mcp_sqlite/tools/
write_query.rs

1//! MCP tool: `writeQuery`.
2
3use std::borrow::Cow;
4
5use database_mcp_server::types::QueryResponse;
6
7use database_mcp_sql::Connection as _;
8use database_mcp_sql::SqlError;
9use rmcp::handler::server::router::tool::{AsyncTool, ToolBase};
10use rmcp::model::{ErrorData, ToolAnnotations};
11
12use crate::SqliteHandler;
13use crate::types::QueryRequest;
14
15/// Marker type for the `writeQuery` MCP tool.
16pub(crate) struct WriteQueryTool;
17
18impl WriteQueryTool {
19    const NAME: &'static str = "writeQuery";
20    const TITLE: &'static str = "Write Query";
21    const DESCRIPTION: &'static str = r#"Execute a write SQL query (INSERT, UPDATE, DELETE, CREATE, ALTER, DROP).
22
23<usecase>
24Use when:
25- Inserting, updating, or deleting rows
26- Creating or altering tables, indexes, views, or other schema objects
27- Any data modification operation
28</usecase>
29
30<when_not_to_use>
31- Read-only queries (SELECT) → use readQuery
32- Query performance analysis → use explainQuery
33</when_not_to_use>
34
35<examples>
36✓ "INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')"
37✓ "UPDATE orders SET status = 'shipped' WHERE id = 42"
38✓ "CREATE TABLE logs (id INTEGER PRIMARY KEY, message TEXT)"
39✗ "SELECT * FROM users" → use readQuery
40</examples>
41
42<what_it_returns>
43A JSON array of affected/returning row objects, each keyed by column name.
44</what_it_returns>"#;
45}
46
47impl ToolBase for WriteQueryTool {
48    type Parameter = QueryRequest;
49    type Output = QueryResponse;
50    type Error = ErrorData;
51
52    fn name() -> Cow<'static, str> {
53        Self::NAME.into()
54    }
55
56    fn title() -> Option<String> {
57        Some(Self::TITLE.into())
58    }
59
60    fn description() -> Option<Cow<'static, str>> {
61        Some(Self::DESCRIPTION.into())
62    }
63
64    fn annotations() -> Option<ToolAnnotations> {
65        Some(
66            ToolAnnotations::new()
67                .read_only(false)
68                .destructive(true)
69                .idempotent(false)
70                .open_world(true),
71        )
72    }
73}
74
75impl AsyncTool<SqliteHandler> for WriteQueryTool {
76    async fn invoke(handler: &SqliteHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
77        Ok(handler.write_query(params).await?)
78    }
79}
80
81impl SqliteHandler {
82    /// Executes a write SQL query.
83    ///
84    /// # Errors
85    ///
86    /// Returns [`SqlError`] if the query fails.
87    pub async fn write_query(&self, QueryRequest { query }: QueryRequest) -> Result<QueryResponse, SqlError> {
88        let rows = self.connection.fetch_json(query.as_str(), None).await?;
89        Ok(QueryResponse { rows })
90    }
91}