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::{ApiError, 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)]
31#[schemars(extend("properties" = {}))]
32pub struct GetSettingsParams {}
33
34#[derive(Debug, Deserialize, schemars::JsonSchema)]
36pub struct UpdateSettingsParams {
37 pub auto_purge_enabled: bool,
39 pub retention_days: i32,
41}
42
43#[tool_router(router = settings_router, vis = "pub")]
44impl crate::mcp::server::YggMcpServer {
45 #[tool(description = "读取站点回收站配置(自动清理开关 + 保留天数)。需要 admin 作用域。")]
47 async fn get_settings(
48 &self,
49 Parameters(_p): Parameters<GetSettingsParams>,
50 Extension(parts): Extension<http::request::Parts>,
51 ) -> Result<CallToolResult, McpError> {
52 require_admin(&parts, "get_settings")?;
53
54 let settings = async {
55 let client = get_conn().await.map_err(AppError::db_conn)?;
56 load_trash_settings(&client).await
57 }
58 .await
59 .map_err(|e| McpError::internal_error(ApiError::from(e).to_string(), None))?;
60
61 let text = serde_json::to_string_pretty(&settings)
62 .map_err(|e| McpError::internal_error(format!("encode failed: {e}"), None))?;
63 Ok(CallToolResult::success(vec![ContentBlock::Text(
64 TextContent::new(text),
65 )]))
66 }
67
68 #[tool(
70 description = "更新站点回收站配置(自动清理开关 + 保留天数)。retention_days 会被钳制到 1..=365。需要 admin 作用域。"
71 )]
72 async fn update_settings(
73 &self,
74 Parameters(UpdateSettingsParams {
75 auto_purge_enabled,
76 retention_days,
77 }): Parameters<UpdateSettingsParams>,
78 Extension(parts): Extension<http::request::Parts>,
79 ) -> Result<CallToolResult, McpError> {
80 require_admin(&parts, "update_settings")?;
81
82 let updated = async {
83 let client = get_conn().await.map_err(AppError::db_conn)?;
84 save_trash_settings(&client, auto_purge_enabled, retention_days).await
85 }
86 .await
87 .map_err(|e| McpError::internal_error(ApiError::from(e).to_string(), None))?;
88
89 tracing::info!(
90 "MCP: trash settings updated: auto_purge={}, retention_days={}",
91 updated.auto_purge_enabled,
92 updated.retention_days
93 );
94
95 let text = serde_json::to_string_pretty(&updated)
96 .map_err(|e| McpError::internal_error(format!("encode failed: {e}"), None))?;
97 Ok(CallToolResult::success(vec![ContentBlock::Text(
98 TextContent::new(text),
99 )]))
100 }
101}