mcp_langbase_reasoning/server/
mod.rs

1//! Server module for MCP protocol handling.
2//!
3//! This module provides:
4//! - MCP server implementation over stdio
5//! - Tool call handlers and routing
6//! - Shared application state management
7//! - Self-improvement system integration
8
9mod handlers;
10mod mcp;
11
12pub use handlers::*;
13pub use mcp::*;
14
15use std::sync::Arc;
16
17use crate::config::Config;
18use crate::langbase::LangbaseClient;
19use crate::modes::{
20    AutoMode, BacktrackingMode, DecisionMode, DetectionMode, DivergentMode, EvidenceMode, GotMode,
21    LinearMode, ReflectionMode, TreeMode,
22};
23use crate::presets::PresetRegistry;
24use crate::self_improvement::{SelfImprovementConfig, SelfImprovementSystem};
25use crate::storage::SqliteStorage;
26
27/// Application state shared across handlers.
28///
29/// Contains all mode handlers and shared resources needed for
30/// processing reasoning requests.
31pub struct AppState {
32    /// Application configuration.
33    pub config: Config,
34    /// SQLite storage backend.
35    pub storage: SqliteStorage,
36    /// Langbase API client.
37    pub langbase: LangbaseClient,
38    /// Linear reasoning mode handler.
39    pub linear_mode: LinearMode,
40    /// Tree reasoning mode handler.
41    pub tree_mode: TreeMode,
42    /// Divergent reasoning mode handler.
43    pub divergent_mode: DivergentMode,
44    /// Reflection reasoning mode handler.
45    pub reflection_mode: ReflectionMode,
46    /// Backtracking mode handler.
47    pub backtracking_mode: BacktrackingMode,
48    /// Auto mode router handler.
49    pub auto_mode: AutoMode,
50    /// Graph-of-Thoughts mode handler.
51    pub got_mode: GotMode,
52    /// Decision framework mode handler.
53    pub decision_mode: DecisionMode,
54    /// Evidence assessment mode handler.
55    pub evidence_mode: EvidenceMode,
56    /// Detection mode handler for bias/fallacy detection.
57    pub detection_mode: DetectionMode,
58    /// Workflow preset registry.
59    pub preset_registry: Arc<PresetRegistry>,
60    /// Self-improvement system (optional, enabled via config).
61    ///
62    /// When enabled, monitors system health and can take autonomous
63    /// actions to improve performance, error rates, and quality.
64    pub self_improvement: Option<Arc<SelfImprovementSystem>>,
65}
66
67impl AppState {
68    /// Create new application state
69    pub fn new(config: Config, storage: SqliteStorage, langbase: LangbaseClient) -> Self {
70        // Debug: Log pipe configuration
71        tracing::info!(
72            detection_pipe = ?config.pipes.detection.as_ref().and_then(|d| d.pipe.as_ref()),
73            decision_pipe = ?config.pipes.decision.as_ref().and_then(|d| d.pipe.as_ref()),
74            linear_pipe = %config.pipes.linear,
75            "AppState initializing with pipe configuration"
76        );
77
78        let linear_mode = LinearMode::new(storage.clone(), langbase.clone(), &config);
79        let tree_mode = TreeMode::new(storage.clone(), langbase.clone(), &config);
80        let divergent_mode = DivergentMode::new(storage.clone(), langbase.clone(), &config);
81        let reflection_mode = ReflectionMode::new(storage.clone(), langbase.clone(), &config);
82        let backtracking_mode = BacktrackingMode::new(storage.clone(), langbase.clone(), &config);
83        let auto_mode = AutoMode::new(storage.clone(), langbase.clone(), &config);
84        let got_mode = GotMode::new(storage.clone(), langbase.clone(), &config);
85        let decision_mode = DecisionMode::new(storage.clone(), langbase.clone(), &config);
86        let evidence_mode = EvidenceMode::new(storage.clone(), langbase.clone(), &config);
87        let detection_mode = DetectionMode::new(storage.clone(), langbase.clone(), &config);
88        let preset_registry = Arc::new(PresetRegistry::new());
89
90        // Initialize self-improvement system (enabled by default)
91        let self_improvement_config = SelfImprovementConfig::from_env();
92        let self_improvement = if self_improvement_config.enabled {
93            tracing::info!("Self-improvement system enabled (autonomous optimization active)");
94            Some(Arc::new(SelfImprovementSystem::new(
95                self_improvement_config,
96                storage.clone(),
97                langbase.clone(),
98            )))
99        } else {
100            tracing::warn!("Self-improvement system disabled via SELF_IMPROVEMENT_ENABLED=false");
101            None
102        };
103
104        Self {
105            config,
106            storage,
107            langbase,
108            linear_mode,
109            tree_mode,
110            divergent_mode,
111            reflection_mode,
112            backtracking_mode,
113            auto_mode,
114            got_mode,
115            decision_mode,
116            evidence_mode,
117            detection_mode,
118            preset_registry,
119            self_improvement,
120        }
121    }
122
123    /// Record an invocation event for self-improvement monitoring.
124    ///
125    /// This should be called after each tool invocation to feed metrics
126    /// to the self-improvement system.
127    pub async fn record_invocation(&self, event: crate::self_improvement::InvocationEvent) {
128        if let Some(ref system) = self.self_improvement {
129            system.on_invocation(event).await;
130        }
131    }
132
133    /// Check if self-improvement system is enabled.
134    pub fn self_improvement_enabled(&self) -> bool {
135        self.self_improvement.is_some()
136    }
137}
138
139// Manual Clone implementation since SelfImprovementSystem is behind Arc
140impl Clone for AppState {
141    fn clone(&self) -> Self {
142        Self {
143            config: self.config.clone(),
144            storage: self.storage.clone(),
145            langbase: self.langbase.clone(),
146            linear_mode: self.linear_mode.clone(),
147            tree_mode: self.tree_mode.clone(),
148            divergent_mode: self.divergent_mode.clone(),
149            reflection_mode: self.reflection_mode.clone(),
150            backtracking_mode: self.backtracking_mode.clone(),
151            auto_mode: self.auto_mode.clone(),
152            got_mode: self.got_mode.clone(),
153            decision_mode: self.decision_mode.clone(),
154            evidence_mode: self.evidence_mode.clone(),
155            detection_mode: self.detection_mode.clone(),
156            preset_registry: Arc::clone(&self.preset_registry),
157            self_improvement: self.self_improvement.as_ref().map(Arc::clone),
158        }
159    }
160}
161
162/// Shared application state handle
163pub type SharedState = Arc<AppState>;
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use crate::config::{
169        DatabaseConfig, ErrorHandlingConfig, LangbaseConfig, LogFormat, LoggingConfig, PipeConfig,
170        RequestConfig,
171    };
172    use std::path::PathBuf;
173
174    fn create_test_config() -> Config {
175        Config {
176            langbase: LangbaseConfig {
177                api_key: "test-key".to_string(),
178                base_url: "https://api.langbase.com".to_string(),
179            },
180            database: DatabaseConfig {
181                path: PathBuf::from(":memory:"),
182                max_connections: 5,
183            },
184            logging: LoggingConfig {
185                level: "info".to_string(),
186                format: LogFormat::Pretty,
187            },
188            request: RequestConfig::default(),
189            pipes: PipeConfig::default(),
190            error_handling: ErrorHandlingConfig::default(),
191        }
192    }
193
194    #[tokio::test]
195    async fn test_app_state_new() {
196        let config = create_test_config();
197        let storage = SqliteStorage::new_in_memory().await.unwrap();
198        let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
199
200        let state = AppState::new(config.clone(), storage, langbase);
201
202        // Verify all modes are initialized
203        assert_eq!(state.config.langbase.api_key, "test-key");
204    }
205
206    #[tokio::test]
207    async fn test_app_state_clone() {
208        let config = create_test_config();
209        let storage = SqliteStorage::new_in_memory().await.unwrap();
210        let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
211
212        let state1 = AppState::new(config, storage, langbase);
213        let state2 = state1.clone();
214
215        assert_eq!(
216            state1.config.langbase.api_key,
217            state2.config.langbase.api_key
218        );
219    }
220
221    #[tokio::test]
222    async fn test_shared_state_type() {
223        let config = create_test_config();
224        let storage = SqliteStorage::new_in_memory().await.unwrap();
225        let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
226
227        let state = AppState::new(config, storage, langbase);
228        let shared: SharedState = Arc::new(state);
229
230        // Verify we can clone the shared state
231        let shared2 = Arc::clone(&shared);
232        assert_eq!(Arc::strong_count(&shared), 2);
233        drop(shared2);
234        assert_eq!(Arc::strong_count(&shared), 1);
235    }
236
237    #[tokio::test]
238    async fn test_app_state_has_all_modes() {
239        let config = create_test_config();
240        let storage = SqliteStorage::new_in_memory().await.unwrap();
241        let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
242
243        let state = AppState::new(config, storage, langbase);
244
245        // Verify preset registry is initialized with builtins
246        assert!(state.preset_registry.count() >= 5);
247    }
248
249    #[tokio::test]
250    async fn test_app_state_storage_access() {
251        use crate::storage::Storage;
252
253        let config = create_test_config();
254        let storage = SqliteStorage::new_in_memory().await.unwrap();
255        let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
256
257        let state = AppState::new(config, storage.clone(), langbase);
258
259        // Verify storage is accessible and usable
260        let session = crate::storage::Session::new("test-metadata");
261        state.storage.create_session(&session).await.unwrap();
262        let retrieved = state.storage.get_session(&session.id).await.unwrap();
263        assert!(retrieved.is_some());
264    }
265
266    #[tokio::test]
267    async fn test_app_state_config_access() {
268        let config = create_test_config();
269        let storage = SqliteStorage::new_in_memory().await.unwrap();
270        let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
271
272        let state = AppState::new(config.clone(), storage, langbase);
273
274        // Verify config values are preserved
275        assert_eq!(state.config.langbase.base_url, "https://api.langbase.com");
276        assert_eq!(state.config.database.max_connections, 5);
277        assert_eq!(state.config.logging.level, "info");
278    }
279}