yggdrasil/mcp/tools/
settings.rs1#![cfg(feature = "server")]
12
13use rmcp::handler::server::tool::Extension;
14use rmcp::handler::server::wrapper::Parameters;
15use rmcp::model::{CallToolResult, ContentBlock, TextContent};
16use rmcp::{schemars, tool, tool_router, ErrorData as McpError};
17
18use super::common::require_admin;
19use serde::Deserialize;
20
21use crate::api::error::AppError;
22use crate::api::settings::{load_trash_settings, save_trash_settings};
23use crate::db::pool::get_conn;
24
25#[derive(Debug, Deserialize, schemars::JsonSchema, Default)]
27pub struct GetSettingsParams {}
28
29#[derive(Debug, Deserialize, schemars::JsonSchema)]
31pub struct UpdateSettingsParams {
32 pub auto_purge_enabled: bool,
34 pub retention_days: i32,
36}
37
38#[tool_router(router = settings_router, vis = "pub")]
39impl crate::mcp::server::YggMcpServer {
40 #[tool(description = "读取站点回收站配置(自动清理开关 + 保留天数)。需要 admin 作用域。")]
42 async fn get_settings(
43 &self,
44 Parameters(_p): Parameters<GetSettingsParams>,
45 Extension(parts): Extension<http::request::Parts>,
46 ) -> Result<CallToolResult, McpError> {
47 require_admin(&parts, "get_settings")?;
48
49 let settings = async {
50 let client = get_conn().await.map_err(AppError::db_conn)?;
51 load_trash_settings(&client).await
52 }
53 .await
54 .map_err(|e| McpError::internal_error(format!("settings read failed: {e:?}"), None))?;
55
56 let text = serde_json::to_string_pretty(&settings)
57 .map_err(|e| McpError::internal_error(format!("encode failed: {e}"), None))?;
58 Ok(CallToolResult::success(vec![ContentBlock::Text(
59 TextContent::new(text),
60 )]))
61 }
62
63 #[tool(
65 description = "更新站点回收站配置(自动清理开关 + 保留天数)。retention_days 会被钳制到 1..=365。需要 admin 作用域。"
66 )]
67 async fn update_settings(
68 &self,
69 Parameters(UpdateSettingsParams {
70 auto_purge_enabled,
71 retention_days,
72 }): Parameters<UpdateSettingsParams>,
73 Extension(parts): Extension<http::request::Parts>,
74 ) -> Result<CallToolResult, McpError> {
75 require_admin(&parts, "update_settings")?;
76
77 let updated = async {
78 let client = get_conn().await.map_err(AppError::db_conn)?;
79 save_trash_settings(&client, auto_purge_enabled, retention_days).await
80 }
81 .await
82 .map_err(|e| McpError::internal_error(format!("settings write failed: {e:?}"), None))?;
83
84 tracing::info!(
85 "MCP: trash settings updated: auto_purge={}, retention_days={}",
86 updated.auto_purge_enabled,
87 updated.retention_days
88 );
89
90 let text = serde_json::to_string_pretty(&updated)
91 .map_err(|e| McpError::internal_error(format!("encode failed: {e}"), None))?;
92 Ok(CallToolResult::success(vec![ContentBlock::Text(
93 TextContent::new(text),
94 )]))
95 }
96}