1use crate::errors::Result as MCPResult;
2use serde_json::{Value, json};
3use tokio_postgres::Client;
4
5const MAX_SETTING_NAME_LEN: usize = 255;
6
7pub async fn show_all_settings(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
9 let rows = client
10 .query(
11 "SELECT name, setting, unit, short_desc, context
12 FROM pg_settings
13 WHERE context NOT LIKE 'internal%'
14 ORDER BY name",
15 &[],
16 )
17 .await?;
18
19 let settings: Vec<Value> = rows
20 .iter()
21 .map(|row| {
22 json!({
23 "name": row.get::<_, String>(0),
24 "value": row.get::<_, Option<String>>(1),
25 "unit": row.get::<_, Option<String>>(2),
26 "description": row.get::<_, String>(3),
27 "context": row.get::<_, String>(4),
28 })
29 })
30 .collect();
31
32 Ok(json!({ "settings": settings }))
33}
34
35pub async fn get_setting(client: &Client, params: &Option<&Value>) -> MCPResult<Value> {
37 let setting_name = params
38 .as_ref()
39 .and_then(|p| {
40 p.get("setting")
41 .and_then(|v| v.as_str())
42 .map(|s| s.to_string())
43 })
44 .ok_or_else(|| {
45 crate::errors::MCPError::InvalidParams("Missing 'setting' parameter".into())
46 })?;
47
48 if setting_name.is_empty() || setting_name.len() > MAX_SETTING_NAME_LEN {
49 return Err(crate::errors::MCPError::InvalidParams(format!(
50 "'setting' must be 1-{MAX_SETTING_NAME_LEN} characters"
51 )));
52 }
53
54 let rows = client
55 .query(
56 "SELECT name, setting, unit, short_desc, context, vartype, source
57 FROM pg_settings
58 WHERE name = $1",
59 &[&setting_name],
60 )
61 .await?;
62
63 if rows.is_empty() {
64 return Err(crate::errors::MCPError::InvalidParams(format!(
65 "Setting not found: {}",
66 setting_name
67 )));
68 }
69
70 let row = &rows[0];
71
72 Ok(json!({
73 "name": row.get::<_, String>(0),
74 "value": row.get::<_, Option<String>>(1),
75 "unit": row.get::<_, Option<String>>(2),
76 "description": row.get::<_, String>(3),
77 "context": row.get::<_, String>(4),
78 "type": row.get::<_, String>(5),
79 "source": row.get::<_, String>(6),
80 }))
81}
82
83pub async fn show_memory_settings(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
85 let rows = client
86 .query(
87 "SELECT name, setting, unit
88 FROM pg_settings
89 WHERE name IN ('shared_buffers', 'effective_cache_size', 'work_mem',
90 'maintenance_work_mem', 'wal_buffers', 'random_page_cost',
91 'effective_io_concurrency')
92 ORDER BY name",
93 &[],
94 )
95 .await?;
96
97 let settings: Vec<Value> = rows
98 .iter()
99 .map(|row| {
100 json!({
101 "name": row.get::<_, String>(0),
102 "value": row.get::<_, Option<String>>(1),
103 "unit": row.get::<_, Option<String>>(2),
104 })
105 })
106 .collect();
107
108 Ok(json!({ "memory_settings": settings }))
109}
110
111pub async fn show_performance_settings(
113 client: &Client,
114 _params: &Option<&Value>,
115) -> MCPResult<Value> {
116 let rows = client
117 .query(
118 "SELECT name, setting, unit
119 FROM pg_settings
120 WHERE name IN ('max_connections', 'checkpoint_timeout', 'checkpoint_completion_target',
121 'wal_level', 'max_wal_senders', 'wal_keep_size', 'synchronous_commit',
122 'constraint_exclusion')
123 ORDER BY name",
124 &[],
125 )
126 .await?;
127
128 let settings: Vec<Value> = rows
129 .iter()
130 .map(|row| {
131 json!({
132 "name": row.get::<_, String>(0),
133 "value": row.get::<_, Option<String>>(1),
134 "unit": row.get::<_, Option<String>>(2),
135 })
136 })
137 .collect();
138
139 Ok(json!({ "performance_settings": settings }))
140}
141
142pub async fn show_log_settings(client: &Client, _params: &Option<&Value>) -> MCPResult<Value> {
144 let rows = client
145 .query(
146 "SELECT name, setting, unit
147 FROM pg_settings
148 WHERE name LIKE 'log%'
149 ORDER BY name",
150 &[],
151 )
152 .await?;
153
154 let settings: Vec<Value> = rows
155 .iter()
156 .map(|row| {
157 json!({
158 "name": row.get::<_, String>(0),
159 "value": row.get::<_, Option<String>>(1),
160 "unit": row.get::<_, Option<String>>(2),
161 })
162 })
163 .collect();
164
165 Ok(json!({ "log_settings": settings }))
166}