database_mcp_postgres/tools/
write_query.rs1use std::borrow::Cow;
4
5use database_mcp_server::AppError;
6use database_mcp_server::types::{QueryRequest, QueryResponse};
7use database_mcp_sql::Connection as _;
8use rmcp::handler::server::router::tool::{AsyncTool, ToolBase};
9use rmcp::model::{ErrorData, ToolAnnotations};
10use serde_json::Value;
11
12use crate::PostgresHandler;
13
14pub(crate) struct WriteQueryTool;
16
17impl WriteQueryTool {
18 const NAME: &'static str = "write_query";
19 const TITLE: &'static str = "Write Query";
20 const DESCRIPTION: &'static str = r#"Execute a write SQL query (INSERT, UPDATE, DELETE, CREATE, ALTER, DROP).
21
22<usecase>
23Use when:
24- Inserting, updating, or deleting rows
25- Creating or altering tables, indexes, views, or other schema objects
26- Any data modification operation
27</usecase>
28
29<when_not_to_use>
30- Read-only queries (SELECT, SHOW) → use read_query
31- Query performance analysis → use explain_query
32- Creating/dropping entire databases → use create_database or drop_database
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 SERIAL PRIMARY KEY, message TEXT)"
39✗ "SELECT * FROM users" → use read_query
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<PostgresHandler> for WriteQueryTool {
76 async fn invoke(handler: &PostgresHandler, params: Self::Parameter) -> Result<Self::Output, Self::Error> {
77 Ok(handler.write_query(¶ms).await?)
78 }
79}
80
81impl PostgresHandler {
82 pub async fn write_query(&self, request: &QueryRequest) -> Result<QueryResponse, AppError> {
88 let db = Some(request.database_name.trim()).filter(|s| !s.is_empty());
89 let rows = self.connection.fetch(request.query.as_str(), db).await?;
90 Ok(QueryResponse {
91 rows: Value::Array(rows),
92 })
93 }
94}