gestura_core_mcp/
lifecycle.rs

1//! MCP Lifecycle Management
2//! Handles initialize/initialized handshake, ping, and shutdown.
3
4use super::types::{
5    ClientCapabilities, ClientInfo, InitializeParams, InitializeResult, LoggingCapability,
6    PROTOCOL_VERSION, PingResult, PromptsCapability, ResourcesCapability, ServerCapabilities,
7    ServerInfo, SessionState, ToolsCapability,
8};
9use std::sync::Arc;
10use std::sync::atomic::{AtomicU8, Ordering};
11
12/// Session manager for MCP lifecycle
13#[derive(Debug)]
14pub struct SessionManager {
15    state: AtomicU8,
16    client_info: std::sync::RwLock<Option<ClientInfo>>,
17    client_capabilities: std::sync::RwLock<Option<ClientCapabilities>>,
18    server_info: ServerInfo,
19    server_capabilities: ServerCapabilities,
20}
21
22impl Default for SessionManager {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl SessionManager {
29    /// Create a new session manager
30    pub fn new() -> Self {
31        Self {
32            state: AtomicU8::new(SessionState::Uninitialized as u8),
33            client_info: std::sync::RwLock::new(None),
34            client_capabilities: std::sync::RwLock::new(None),
35            server_info: ServerInfo::default(),
36            server_capabilities: Self::default_capabilities(),
37        }
38    }
39
40    /// Get default server capabilities
41    fn default_capabilities() -> ServerCapabilities {
42        ServerCapabilities {
43            tools: Some(ToolsCapability { list_changed: true }),
44            resources: Some(ResourcesCapability {
45                subscribe: false,
46                list_changed: true,
47            }),
48            prompts: Some(PromptsCapability { list_changed: true }),
49            logging: Some(LoggingCapability {}),
50        }
51    }
52
53    /// Get current session state
54    pub fn state(&self) -> SessionState {
55        match self.state.load(Ordering::SeqCst) {
56            0 => SessionState::Uninitialized,
57            1 => SessionState::Initializing,
58            2 => SessionState::Ready,
59            3 => SessionState::ShuttingDown,
60            4 => SessionState::Closed,
61            _ => SessionState::Uninitialized,
62        }
63    }
64
65    /// Set session state
66    fn set_state(&self, state: SessionState) {
67        self.state.store(state as u8, Ordering::SeqCst);
68    }
69
70    /// Check if session is ready for requests
71    pub fn is_ready(&self) -> bool {
72        self.state() == SessionState::Ready
73    }
74
75    /// Handle initialize request
76    pub fn initialize(&self, params: InitializeParams) -> Result<InitializeResult, String> {
77        let current_state = self.state();
78        if current_state != SessionState::Uninitialized {
79            return Err(format!(
80                "Cannot initialize: session is in {:?} state",
81                current_state
82            ));
83        }
84
85        // Validate protocol version
86        if params.protocol_version != PROTOCOL_VERSION {
87            tracing::warn!(
88                "Client protocol version {} differs from server version {}",
89                params.protocol_version,
90                PROTOCOL_VERSION
91            );
92        }
93
94        // Store client info
95        if let Ok(mut info) = self.client_info.write() {
96            *info = Some(params.client_info.clone());
97        }
98
99        // Store client capabilities
100        if let Ok(mut caps) = self.client_capabilities.write() {
101            *caps = Some(params.capabilities);
102        }
103
104        self.set_state(SessionState::Initializing);
105
106        tracing::info!(
107            "MCP session initializing with client: {} v{}",
108            params.client_info.name,
109            params.client_info.version
110        );
111
112        Ok(InitializeResult {
113            protocol_version: PROTOCOL_VERSION.to_string(),
114            capabilities: self.server_capabilities.clone(),
115            server_info: self.server_info.clone(),
116            instructions: Some(
117                "Gestura is a voice-first AI assistant with haptic feedback support.".to_string(),
118            ),
119        })
120    }
121
122    /// Handle initialized notification (completes handshake)
123    pub fn initialized(&self) -> Result<(), String> {
124        let current_state = self.state();
125        if current_state != SessionState::Initializing {
126            return Err(format!(
127                "Cannot complete initialization: session is in {:?} state",
128                current_state
129            ));
130        }
131
132        self.set_state(SessionState::Ready);
133        tracing::info!("MCP session initialized and ready");
134        Ok(())
135    }
136
137    /// Handle ping request
138    pub fn ping(&self) -> PingResult {
139        PingResult {}
140    }
141
142    /// Handle shutdown request
143    pub fn shutdown(&self) -> Result<(), String> {
144        let current_state = self.state();
145        if current_state == SessionState::Closed {
146            return Err("Session already closed".to_string());
147        }
148
149        self.set_state(SessionState::ShuttingDown);
150        tracing::info!("MCP session shutting down");
151
152        // Clean up resources
153        if let Ok(mut info) = self.client_info.write() {
154            *info = None;
155        }
156        if let Ok(mut caps) = self.client_capabilities.write() {
157            *caps = None;
158        }
159
160        self.set_state(SessionState::Closed);
161        tracing::info!("MCP session closed");
162        Ok(())
163    }
164
165    /// Get client info if available
166    pub fn client_info(&self) -> Option<ClientInfo> {
167        self.client_info.read().ok()?.clone()
168    }
169
170    /// Get client capabilities if available
171    pub fn client_capabilities(&self) -> Option<ClientCapabilities> {
172        self.client_capabilities.read().ok()?.clone()
173    }
174
175    /// Get server info
176    pub fn server_info(&self) -> &ServerInfo {
177        &self.server_info
178    }
179
180    /// Get server capabilities
181    pub fn server_capabilities(&self) -> &ServerCapabilities {
182        &self.server_capabilities
183    }
184}
185
186/// Create a shared session manager
187pub fn create_session_manager() -> Arc<SessionManager> {
188    Arc::new(SessionManager::new())
189}