turul_http_mcp_server/middleware/
context.rs

1//! Request context and session injection types
2
3use serde_json::{Value, Map};
4use std::collections::HashMap;
5
6/// Normalized request context across all transports (HTTP, Lambda, etc.)
7///
8/// Provides uniform access to request data regardless of transport mechanism.
9///
10/// # Examples
11///
12/// ```rust
13/// use turul_http_mcp_server::middleware::RequestContext;
14/// use serde_json::json;
15///
16/// let mut ctx = RequestContext::new(
17///     "tools/call",
18///     Some(json!({"name": "calculator"})),
19/// );
20///
21/// ctx.add_metadata("user-agent", json!("Claude-Code/1.0"));
22///
23/// assert_eq!(ctx.method(), "tools/call");
24/// assert!(ctx.params().is_some());
25/// assert_eq!(ctx.metadata().get("user-agent").unwrap(), "Claude-Code/1.0");
26/// ```
27#[derive(Debug, Clone)]
28pub struct RequestContext<'a> {
29    /// MCP method name (e.g., "tools/call", "resources/read")
30    method: &'a str,
31
32    /// Request parameters (JSON-RPC params field)
33    params: Option<Value>,
34
35    /// Transport-specific metadata (HTTP headers, Lambda event fields, etc.)
36    metadata: Map<String, Value>,
37}
38
39impl<'a> RequestContext<'a> {
40    /// Create a new request context
41    ///
42    /// # Parameters
43    ///
44    /// - `method`: MCP method name (e.g., "tools/call")
45    /// - `params`: Optional request parameters
46    pub fn new(method: &'a str, params: Option<Value>) -> Self {
47        Self {
48            method,
49            params,
50            metadata: Map::new(),
51        }
52    }
53
54    /// Get the MCP method name
55    pub fn method(&self) -> &str {
56        self.method
57    }
58
59    /// Get request parameters (if any)
60    pub fn params(&self) -> Option<&Value> {
61        self.params.as_ref()
62    }
63
64    /// Get mutable request parameters
65    pub fn params_mut(&mut self) -> Option<&mut Value> {
66        self.params.as_mut()
67    }
68
69    /// Get transport metadata (read-only)
70    pub fn metadata(&self) -> &Map<String, Value> {
71        &self.metadata
72    }
73
74    /// Add metadata entry
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// use turul_http_mcp_server::middleware::RequestContext;
80    /// use serde_json::json;
81    ///
82    /// let mut ctx = RequestContext::new("tools/call", None);
83    /// ctx.add_metadata("client-ip", json!("127.0.0.1"));
84    /// ```
85    pub fn add_metadata(&mut self, key: impl Into<String>, value: Value) {
86        self.metadata.insert(key.into(), value);
87    }
88}
89
90/// Write-only mechanism for middleware to populate session state
91///
92/// Prevents middleware from interfering with core session management while
93/// allowing controlled injection of custom state and metadata.
94///
95/// # Design
96///
97/// - **Write-only**: Middleware cannot read existing session state
98/// - **Deferred application**: Changes applied after all middleware succeed
99/// - **Isolation**: Each middleware's changes are independent
100///
101/// # Examples
102///
103/// ```rust
104/// use turul_http_mcp_server::middleware::SessionInjection;
105/// use serde_json::json;
106///
107/// let mut injection = SessionInjection::new();
108///
109/// // Set typed state
110/// injection.set_state("user_id", json!(12345));
111/// injection.set_state("role", json!("admin"));
112///
113/// // Set metadata
114/// injection.set_metadata("authenticated_at", json!("2025-10-04T12:00:00Z"));
115///
116/// // State and metadata are applied to session after middleware succeeds
117/// assert!(!injection.is_empty());
118/// ```
119#[derive(Debug, Default, Clone)]
120pub struct SessionInjection {
121    /// State entries to inject into session
122    state: HashMap<String, Value>,
123
124    /// Metadata entries to inject into session
125    metadata: HashMap<String, Value>,
126}
127
128impl SessionInjection {
129    /// Create a new empty session injection
130    pub fn new() -> Self {
131        Self::default()
132    }
133
134    /// Set a state entry
135    ///
136    /// # Parameters
137    ///
138    /// - `key`: State key (used with `SessionContext::get_typed_state()`)
139    /// - `value`: JSON value to store
140    pub fn set_state(&mut self, key: impl Into<String>, value: Value) {
141        self.state.insert(key.into(), value);
142    }
143
144    /// Set a metadata entry
145    ///
146    /// # Parameters
147    ///
148    /// - `key`: Metadata key
149    /// - `value`: JSON value to store
150    pub fn set_metadata(&mut self, key: impl Into<String>, value: Value) {
151        self.metadata.insert(key.into(), value);
152    }
153
154    /// Get all state entries (for internal use)
155    pub(crate) fn state(&self) -> &HashMap<String, Value> {
156        &self.state
157    }
158
159    /// Get all metadata entries (for internal use)
160    pub(crate) fn metadata(&self) -> &HashMap<String, Value> {
161        &self.metadata
162    }
163
164    /// Check if injection is empty
165    pub fn is_empty(&self) -> bool {
166        self.state.is_empty() && self.metadata.is_empty()
167    }
168}
169
170/// Result from the MCP dispatcher (success or error)
171///
172/// Middleware can inspect and modify this result in `after_dispatch()`.
173///
174/// # Examples
175///
176/// ```rust
177/// use turul_http_mcp_server::middleware::DispatcherResult;
178/// use serde_json::json;
179///
180/// let mut result = DispatcherResult::Success(json!({"output": "Hello"}));
181///
182/// // Middleware can transform successful responses
183/// if let DispatcherResult::Success(ref mut value) = result {
184///     if let Some(obj) = value.as_object_mut() {
185///         obj.insert("timestamp".to_string(), json!("2025-10-04T12:00:00Z"));
186///     }
187/// }
188/// ```
189#[derive(Debug, Clone)]
190pub enum DispatcherResult {
191    /// Successful response (JSON-RPC result field)
192    Success(Value),
193
194    /// Error response (will be converted to JSON-RPC error)
195    Error(String),
196}
197
198impl DispatcherResult {
199    /// Check if result is successful
200    pub fn is_success(&self) -> bool {
201        matches!(self, Self::Success(_))
202    }
203
204    /// Check if result is an error
205    pub fn is_error(&self) -> bool {
206        matches!(self, Self::Error(_))
207    }
208
209    /// Get success value (if any)
210    pub fn success(&self) -> Option<&Value> {
211        match self {
212            Self::Success(v) => Some(v),
213            Self::Error(_) => None,
214        }
215    }
216
217    /// Get mutable success value (if any)
218    pub fn success_mut(&mut self) -> Option<&mut Value> {
219        match self {
220            Self::Success(v) => Some(v),
221            Self::Error(_) => None,
222        }
223    }
224
225    /// Get error message (if any)
226    pub fn error(&self) -> Option<&str> {
227        match self {
228            Self::Success(_) => None,
229            Self::Error(e) => Some(e),
230        }
231    }
232}