Skip to content
Clone

feat(config): add mcp section to central lib/config crate and refresh app/lib wiring

zhenyi committed
parent 5562e7e commit 087d727 Browse files

4 files changed

+64 −1

File tree 4
Modified lib/config/src/app.rs View lib/config/src/app.rs
@@ -9,6 +9,7 @@
99use crate::issues;
1010use crate::jwt;
1111use crate::live;
12use crate::mcp;
1213use crate::obs;
1314use crate::payment;
1415use crate::pow;
@@ -51,6 +52,7 @@
5152 pub git_sync: git::SyncSettings,
5253 pub webhook: webhook::WebhookConfig,
5354 pub obs: obs::ObsSettings,
55 pub mcp: mcp::McpSettings,
5456}
5557
5658impl AppConfig {
@@ -60,6 +62,7 @@
6062 self.search.is_valid()?;
6163 self.webhook.is_valid()?;
6264 self.obs.is_valid()?;
65 self.mcp.is_valid()?;
6366 if self.chat.topic_prefix != self.gateway.topic_prefix {
6467 return Err(format!(
6568 "chat.topic_prefix ({:?}) must equal gateway.topic_prefix ({:?})",
Modified lib/config/src/lib.rs View lib/config/src/lib.rs
@@ -11,6 +11,7 @@
1111pub mod jwt;
1212pub mod live;
1313pub mod loader;
14pub mod mcp;
1415pub mod obs;
1516pub mod payment;
1617pub mod pow;
Added lib/config/src/mcp.rs View lib/config/src/mcp.rs
@@ -0,0 +1,58 @@
1use serde::{Deserialize, Serialize};
2
3/// (`Authorization: Bearer <user-access-key>`), verified against
4/// plus `internal_allowed_cidrs`).
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(default)]
7pub struct McpSettings {
8 pub enabled: bool,
9 pub external_path: String,
10 pub internal_path: String,
11 pub internal_secret_key: Option<String>,
12 /// Extra allowed CIDRs appended to the built-in whitelist (e.g. custom
13 pub internal_allowed_cidrs: Vec<String>,
14 pub trust_proxy: bool,
15 pub system_user_id: Option<String>,
16 pub max_request_bytes: usize,
17}
18
19impl Default for McpSettings {
20 fn default() -> Self {
21 Self {
22 enabled: true,
23 external_path: "mcp".to_string(),
24 internal_path: "internal/mcp".to_string(),
25 internal_secret_key: None,
26 internal_allowed_cidrs: Vec::new(),
27 trust_proxy: false,
28 system_user_id: None,
29 max_request_bytes: 1 << 20,
30 }
31 }
32}
33
34impl McpSettings {
35 pub fn is_valid(&self) -> Result<(), String> {
36 if !self.enabled {
37 return Ok(());
38 }
39 if self.external_path.is_empty() || self.internal_path.is_empty() {
40 return Err(
41 "mcp.external_path / mcp.internal_path must not be empty when mcp.enabled".into(),
42 );
43 }
44 for cidr in &self.internal_allowed_cidrs {
45 cidr.parse::<ipnet::IpNet>()
46 .map_err(|e| format!("mcp.internal_allowed_cidrs entry {cidr:?} invalid: {e}"))?;
47 }
48 if let Some(secret) = &self.internal_secret_key {
49 if secret.len() < 16 {
50 return Err("mcp.internal_secret_key must be at least 16 characters".into());
51 }
52 }
53 if self.max_request_bytes < 1024 {
54 return Err("mcp.max_request_bytes must be >= 1024".into());
55 }
56 Ok(())
57 }
58}
No newline at end of file
Modified lib/config/Cargo.toml View lib/config/Cargo.toml
@@ -20,4 +20,5 @@
2020utoipa = { workspace = true }
2121thiserror = { workspace = true }
2222config-rs = { workspace = true }
23dotenvy = "0.15"
No newline at end of file
23dotenvy = "0.15"
24ipnet = "2.12"
No newline at end of file