1use std::collections::HashMap;
5
6use serde::{Deserialize, Serialize};
7
8use crate::defaults::default_true;
9
10pub use zeph_mcp::McpTrustLevel;
11
12fn default_slack_port() -> u16 {
13 3000
14}
15
16fn default_slack_webhook_host() -> String {
17 "127.0.0.1".into()
18}
19
20fn default_a2a_host() -> String {
21 "0.0.0.0".into()
22}
23
24fn default_a2a_port() -> u16 {
25 8080
26}
27
28fn default_a2a_rate_limit() -> u32 {
29 60
30}
31
32fn default_a2a_max_body() -> usize {
33 1_048_576
34}
35
36fn default_max_dynamic_servers() -> usize {
37 10
38}
39
40fn default_mcp_timeout() -> u64 {
41 30
42}
43
44fn default_oauth_callback_port() -> u16 {
45 18766
46}
47
48fn default_oauth_client_name() -> String {
49 "Zeph".into()
50}
51
52#[derive(Clone, Deserialize, Serialize)]
53pub struct TelegramConfig {
54 pub token: Option<String>,
55 #[serde(default)]
56 pub allowed_users: Vec<String>,
57}
58
59impl std::fmt::Debug for TelegramConfig {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("TelegramConfig")
62 .field("token", &self.token.as_ref().map(|_| "[REDACTED]"))
63 .field("allowed_users", &self.allowed_users)
64 .finish()
65 }
66}
67
68#[derive(Clone, Deserialize, Serialize)]
69pub struct DiscordConfig {
70 pub token: Option<String>,
71 pub application_id: Option<String>,
72 #[serde(default)]
73 pub allowed_user_ids: Vec<String>,
74 #[serde(default)]
75 pub allowed_role_ids: Vec<String>,
76 #[serde(default)]
77 pub allowed_channel_ids: Vec<String>,
78}
79
80impl std::fmt::Debug for DiscordConfig {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 f.debug_struct("DiscordConfig")
83 .field("token", &self.token.as_ref().map(|_| "[REDACTED]"))
84 .field("application_id", &self.application_id)
85 .field("allowed_user_ids", &self.allowed_user_ids)
86 .field("allowed_role_ids", &self.allowed_role_ids)
87 .field("allowed_channel_ids", &self.allowed_channel_ids)
88 .finish()
89 }
90}
91
92#[derive(Clone, Deserialize, Serialize)]
93pub struct SlackConfig {
94 pub bot_token: Option<String>,
95 pub signing_secret: Option<String>,
96 #[serde(default = "default_slack_webhook_host")]
97 pub webhook_host: String,
98 #[serde(default = "default_slack_port")]
99 pub port: u16,
100 #[serde(default)]
101 pub allowed_user_ids: Vec<String>,
102 #[serde(default)]
103 pub allowed_channel_ids: Vec<String>,
104}
105
106impl std::fmt::Debug for SlackConfig {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 f.debug_struct("SlackConfig")
109 .field("bot_token", &self.bot_token.as_ref().map(|_| "[REDACTED]"))
110 .field(
111 "signing_secret",
112 &self.signing_secret.as_ref().map(|_| "[REDACTED]"), )
114 .field("webhook_host", &self.webhook_host)
115 .field("port", &self.port)
116 .field("allowed_user_ids", &self.allowed_user_ids)
117 .field("allowed_channel_ids", &self.allowed_channel_ids)
118 .finish()
119 }
120}
121
122#[derive(Deserialize, Serialize)]
123pub struct A2aServerConfig {
124 #[serde(default)]
125 pub enabled: bool,
126 #[serde(default = "default_a2a_host")]
127 pub host: String,
128 #[serde(default = "default_a2a_port")]
129 pub port: u16,
130 #[serde(default)]
131 pub public_url: String,
132 #[serde(default)]
133 pub auth_token: Option<String>,
134 #[serde(default = "default_a2a_rate_limit")]
135 pub rate_limit: u32,
136 #[serde(default = "default_true")]
137 pub require_tls: bool,
138 #[serde(default = "default_true")]
139 pub ssrf_protection: bool,
140 #[serde(default = "default_a2a_max_body")]
141 pub max_body_size: usize,
142}
143
144impl std::fmt::Debug for A2aServerConfig {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 f.debug_struct("A2aServerConfig")
147 .field("enabled", &self.enabled)
148 .field("host", &self.host)
149 .field("port", &self.port)
150 .field("public_url", &self.public_url)
151 .field(
152 "auth_token",
153 &self.auth_token.as_ref().map(|_| "[REDACTED]"),
154 )
155 .field("rate_limit", &self.rate_limit)
156 .field("require_tls", &self.require_tls)
157 .field("ssrf_protection", &self.ssrf_protection)
158 .field("max_body_size", &self.max_body_size)
159 .finish()
160 }
161}
162
163impl Default for A2aServerConfig {
164 fn default() -> Self {
165 Self {
166 enabled: false,
167 host: default_a2a_host(),
168 port: default_a2a_port(),
169 public_url: String::new(),
170 auth_token: None,
171 rate_limit: default_a2a_rate_limit(),
172 require_tls: true,
173 ssrf_protection: true,
174 max_body_size: default_a2a_max_body(),
175 }
176 }
177}
178
179#[derive(Debug, Clone, Default, Deserialize, Serialize)]
180pub struct McpConfig {
181 #[serde(default)]
182 pub servers: Vec<McpServerConfig>,
183 #[serde(default)]
184 pub allowed_commands: Vec<String>,
185 #[serde(default = "default_max_dynamic_servers")]
186 pub max_dynamic_servers: usize,
187}
188
189#[derive(Clone, Deserialize, Serialize)]
190pub struct McpServerConfig {
191 pub id: String,
192 pub command: Option<String>,
194 #[serde(default)]
195 pub args: Vec<String>,
196 #[serde(default)]
197 pub env: HashMap<String, String>,
198 pub url: Option<String>,
200 #[serde(default = "default_mcp_timeout")]
201 pub timeout: u64,
202 #[serde(default)]
204 pub policy: zeph_mcp::McpPolicy,
205 #[serde(default)]
208 pub headers: HashMap<String, String>,
209 #[serde(default)]
211 pub oauth: Option<McpOAuthConfig>,
212 #[serde(default)]
214 pub trust_level: McpTrustLevel,
215 #[serde(default)]
220 pub tool_allowlist: Vec<String>,
221}
222
223#[derive(Debug, Clone, Deserialize, Serialize)]
225pub struct McpOAuthConfig {
226 #[serde(default)]
228 pub enabled: bool,
229 #[serde(default)]
231 pub token_storage: OAuthTokenStorage,
232 #[serde(default)]
234 pub scopes: Vec<String>,
235 #[serde(default = "default_oauth_callback_port")]
237 pub callback_port: u16,
238 #[serde(default = "default_oauth_client_name")]
240 pub client_name: String,
241}
242
243impl Default for McpOAuthConfig {
244 fn default() -> Self {
245 Self {
246 enabled: false,
247 token_storage: OAuthTokenStorage::default(),
248 scopes: Vec::new(),
249 callback_port: default_oauth_callback_port(),
250 client_name: default_oauth_client_name(),
251 }
252 }
253}
254
255#[derive(Debug, Clone, Default, Deserialize, Serialize)]
257#[serde(rename_all = "lowercase")]
258pub enum OAuthTokenStorage {
259 #[default]
261 Vault,
262 Memory,
264}
265
266impl std::fmt::Debug for McpServerConfig {
267 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268 let redacted_env: HashMap<&str, &str> = self
269 .env
270 .keys()
271 .map(|k| (k.as_str(), "[REDACTED]"))
272 .collect();
273 let redacted_headers: HashMap<&str, &str> = self
275 .headers
276 .keys()
277 .map(|k| (k.as_str(), "[REDACTED]"))
278 .collect();
279 f.debug_struct("McpServerConfig")
280 .field("id", &self.id)
281 .field("command", &self.command)
282 .field("args", &self.args)
283 .field("env", &redacted_env)
284 .field("url", &self.url)
285 .field("timeout", &self.timeout)
286 .field("policy", &self.policy)
287 .field("headers", &redacted_headers)
288 .field("oauth", &self.oauth)
289 .field("trust_level", &self.trust_level)
290 .field("tool_allowlist", &self.tool_allowlist)
291 .finish()
292 }
293}