Skip to main content

mcpx/protocol/
messages.rs

1//! Basic message types for the MCP protocol
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6use super::ProgressToken;
7
8/// A request that expects a response.
9pub trait Request {
10    /// Method name constant
11    const METHOD: &'static str;
12
13    /// Get the method name
14    fn method(&self) -> &str;
15
16    /// Get the parameters
17    fn params(&self) -> Option<&serde_json::Value>;
18}
19
20/// A concrete request implementation
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
22pub struct RequestImpl {
23    /// Method name
24    pub method: String,
25    /// Optional parameters
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub params: Option<serde_json::Value>,
28}
29
30impl Request for RequestImpl {
31    const METHOD: &'static str = "";
32
33    fn method(&self) -> &str {
34        &self.method
35    }
36
37    fn params(&self) -> Option<&serde_json::Value> {
38        self.params.as_ref()
39    }
40}
41
42impl RequestImpl {
43    /// Create a new request
44    pub fn new(method: impl Into<String>) -> Self {
45        Self {
46            method: method.into(),
47            params: None,
48        }
49    }
50
51    /// Create a new request with parameters
52    pub fn with_params(method: impl Into<String>, params: serde_json::Value) -> Self {
53        Self {
54            method: method.into(),
55            params: Some(params),
56        }
57    }
58}
59
60impl RequestImpl {
61    /// Create a new request with a progress token
62    pub fn with_progress_token(method: impl Into<String>, progress_token: ProgressToken) -> Self {
63        let mut params = serde_json::Map::new();
64        params.insert("progressToken".to_string(), serde_json::to_value(progress_token).unwrap());
65        Self {
66            method: method.into(),
67            params: Some(serde_json::Value::Object(params)),
68        }
69    }
70}
71
72/// A notification that does not expect a response.
73pub trait Notification {
74    /// Method name constant
75    const METHOD: &'static str;
76
77    /// Get the method name
78    fn method(&self) -> &str;
79
80    /// Get the parameters
81    fn params(&self) -> Option<&serde_json::Value>;
82}
83
84/// A concrete notification implementation
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86pub struct NotificationImpl {
87    /// Method name
88    pub method: String,
89    /// Optional parameters
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub params: Option<serde_json::Value>,
92}
93
94impl Notification for NotificationImpl {
95    const METHOD: &'static str = "";
96
97    fn method(&self) -> &str {
98        &self.method
99    }
100
101    fn params(&self) -> Option<&serde_json::Value> {
102        self.params.as_ref()
103    }
104}
105
106impl NotificationImpl {
107    /// Create a new notification
108    pub fn new(method: impl Into<String>) -> Self {
109        Self {
110            method: method.into(),
111            params: None,
112        }
113    }
114
115    /// Create a new notification with parameters
116    pub fn with_params(method: impl Into<String>, params: serde_json::Value) -> Self {
117        Self {
118            method: method.into(),
119            params: Some(params),
120        }
121    }
122}
123
124/// Parameters for a request
125#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
126pub struct RequestParams {
127    /// Metadata for the request
128    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
129    pub meta: Option<RequestMeta>,
130    /// Additional parameters (method-specific)
131    #[serde(flatten)]
132    pub additional: HashMap<String, serde_json::Value>,
133}
134
135impl RequestParams {
136    /// Create empty request parameters
137    pub fn new() -> Self {
138        Self {
139            meta: None,
140            additional: HashMap::new(),
141        }
142    }
143
144    /// Add a progress token to the request parameters
145    pub fn set_progress_token(&mut self, token: ProgressToken) {
146        if self.meta.is_none() {
147            self.meta = Some(RequestMeta {
148                progress_token: Some(token),
149            });
150        } else if let Some(meta) = &mut self.meta {
151            meta.progress_token = Some(token);
152        }
153    }
154
155    /// Add a parameter to the request
156    pub fn add<T: Serialize>(&mut self, name: impl Into<String>, value: &T) -> std::result::Result<(), serde_json::Error> {
157        self.additional.insert(name.into(), serde_json::to_value(value)?);
158        Ok(())
159    }
160}
161
162impl Default for RequestParams {
163    fn default() -> Self {
164        Self::new()
165    }
166}
167
168/// Metadata for a request
169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
170pub struct RequestMeta {
171    /// Progress token for the request
172    #[serde(rename = "progressToken", skip_serializing_if = "Option::is_none")]
173    pub progress_token: Option<ProgressToken>,
174}
175
176/// Parameters for a notification
177#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
178pub struct NotificationParams {
179    /// Metadata for the notification
180    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
181    pub meta: Option<NotificationMeta>,
182    /// Additional parameters (method-specific)
183    #[serde(flatten)]
184    pub additional: HashMap<String, serde_json::Value>,
185}
186
187impl NotificationParams {
188    /// Create empty notification parameters
189    pub fn new() -> Self {
190        Self {
191            meta: None,
192            additional: HashMap::new(),
193        }
194    }
195
196    /// Add a parameter to the notification
197    pub fn add<T: Serialize>(&mut self, name: impl Into<String>, value: &T) -> std::result::Result<(), serde_json::Error> {
198        self.additional.insert(name.into(), serde_json::to_value(value)?);
199        Ok(())
200    }
201}
202
203impl Default for NotificationParams {
204    fn default() -> Self {
205        Self::new()
206    }
207}
208
209/// Metadata for a notification
210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
211pub struct NotificationMeta {
212    /// Additional metadata (method-specific)
213    #[serde(flatten)]
214    pub additional: HashMap<String, serde_json::Value>,
215}
216
217impl NotificationMeta {
218    /// Create empty notification metadata
219    pub fn new() -> Self {
220        Self {
221            additional: HashMap::new(),
222        }
223    }
224
225    /// Add a metadata field to the notification
226    pub fn add<T: Serialize>(&mut self, name: impl Into<String>, value: &T) -> std::result::Result<(), serde_json::Error> {
227        self.additional.insert(name.into(), serde_json::to_value(value)?);
228        Ok(())
229    }
230}
231
232impl Default for NotificationMeta {
233    fn default() -> Self {
234        Self::new()
235    }
236}
237
238/// A result returned by a request handler
239pub trait MessageResult {}
240
241/// A concrete result implementation
242#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
243pub struct Result {
244    /// Metadata for the result
245    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
246    pub meta: Option<ResultMeta>,
247    /// Additional fields (method-specific)
248    #[serde(flatten)]
249    pub additional: HashMap<String, serde_json::Value>,
250}
251
252impl MessageResult for Result {}
253
254impl Result {
255    /// Create an empty result
256    pub fn new() -> Self {
257        Self {
258            meta: None,
259            additional: HashMap::new(),
260        }
261    }
262
263    /// Add a field to the result
264    pub fn add<T: Serialize>(&mut self, name: impl Into<String>, value: &T) -> std::result::Result<(), serde_json::Error> {
265        self.additional.insert(name.into(), serde_json::to_value(value)?);
266        Ok(())
267    }
268}
269
270impl Default for Result {
271    fn default() -> Self {
272        Self::new()
273    }
274}
275
276/// Metadata for a result
277#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
278pub struct ResultMeta {
279    /// Additional metadata (method-specific)
280    #[serde(flatten)]
281    pub additional: HashMap<String, serde_json::Value>,
282}
283
284impl ResultMeta {
285    /// Create empty result metadata
286    pub fn new() -> Self {
287        Self {
288            additional: HashMap::new(),
289        }
290    }
291
292    /// Add a metadata field to the result
293    pub fn add<T: Serialize>(&mut self, name: impl Into<String>, value: &T) -> std::result::Result<(), serde_json::Error> {
294        self.additional.insert(name.into(), serde_json::to_value(value)?);
295        Ok(())
296    }
297}
298
299impl Default for ResultMeta {
300    fn default() -> Self {
301        Self::new()
302    }
303}
304
305/// An empty result with no additional fields
306pub type EmptyResult = Result;
307
308/// A paginated request with a cursor parameter
309#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
310pub struct PaginatedRequest {
311    /// Method name
312    pub method: String,
313    /// Optional parameters
314    #[serde(skip_serializing_if = "Option::is_none")]
315    pub params: Option<PaginatedRequestParams>,
316}
317
318impl PaginatedRequest {
319    /// Create a new paginated request
320    pub fn new(method: impl Into<String>) -> Self {
321        Self {
322            method: method.into(),
323            params: None,
324        }
325    }
326
327    /// Create a new paginated request with a cursor
328    pub fn with_cursor(method: impl Into<String>, cursor: impl Into<String>) -> Self {
329        let params = PaginatedRequestParams {
330            cursor: Some(cursor.into()),
331            meta: None,
332            additional: HashMap::new(),
333        };
334
335        Self {
336            method: method.into(),
337            params: Some(params),
338        }
339    }
340}
341
342/// Parameters for a paginated request
343#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
344pub struct PaginatedRequestParams {
345    /// Cursor for pagination
346    #[serde(skip_serializing_if = "Option::is_none")]
347    pub cursor: Option<String>,
348    /// Metadata for the request
349    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
350    pub meta: Option<RequestMeta>,
351    /// Additional parameters (method-specific)
352    #[serde(flatten)]
353    pub additional: HashMap<String, serde_json::Value>,
354}
355
356/// A paginated result with a next cursor
357#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
358pub struct PaginatedResult {
359    /// Cursor for the next page, if any
360    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
361    pub next_cursor: Option<String>,
362    /// Metadata for the result
363    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
364    pub meta: Option<ResultMeta>,
365    /// Additional fields (method-specific)
366    #[serde(flatten)]
367    pub additional: HashMap<String, serde_json::Value>,
368}
369
370impl PaginatedResult {
371    /// Create a new paginated result
372    pub fn new() -> Self {
373        Self {
374            next_cursor: None,
375            meta: None,
376            additional: HashMap::new(),
377        }
378    }
379
380    /// Create a new paginated result with a next cursor
381    pub fn with_next_cursor(next_cursor: impl Into<String>) -> Self {
382        Self {
383            next_cursor: Some(next_cursor.into()),
384            meta: None,
385            additional: HashMap::new(),
386        }
387    }
388
389    /// Add a field to the result
390    pub fn add<T: Serialize>(&mut self, name: impl Into<String>, value: &T) -> std::result::Result<(), serde_json::Error> {
391        self.additional.insert(name.into(), serde_json::to_value(value)?);
392        Ok(())
393    }
394}
395
396impl Default for PaginatedResult {
397    fn default() -> Self {
398        Self::new()
399    }
400}
401
402/// Standard MCP cancel notification
403#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
404pub struct CancelledNotification {
405    /// Method is always "notifications/cancelled"
406    pub method: String,
407    /// Parameters for the cancellation
408    pub params: CancelledParams,
409}
410
411/// Parameters for a cancel notification
412#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
413pub struct CancelledParams {
414    /// The ID of the request to cancel
415    #[serde(rename = "requestId")]
416    pub request_id: super::RequestId,
417    /// Optional reason for cancellation
418    #[serde(skip_serializing_if = "Option::is_none")]
419    pub reason: Option<String>,
420}
421
422impl CancelledNotification {
423    /// Create a new cancelled notification
424    pub fn new<I: Into<super::RequestId>>(request_id: I) -> Self {
425        Self {
426            method: "notifications/cancelled".to_string(),
427            params: CancelledParams {
428                request_id: request_id.into(),
429                reason: None,
430            },
431        }
432    }
433
434    /// Create a new cancelled notification with a reason
435    pub fn with_reason<I: Into<super::RequestId>>(request_id: I, reason: impl Into<String>) -> Self {
436        Self {
437            method: "notifications/cancelled".to_string(),
438            params: CancelledParams {
439                request_id: request_id.into(),
440                reason: Some(reason.into()),
441            },
442        }
443    }
444}
445
446/// Standard MCP progress notification
447#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
448pub struct ProgressNotification {
449    /// Method is always "notifications/progress"
450    pub method: String,
451    /// Parameters for the progress notification
452    pub params: ProgressParams,
453}
454
455/// Parameters for a progress notification
456#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
457pub struct ProgressParams {
458    /// Progress token from the original request
459    #[serde(rename = "progressToken")]
460    pub progress_token: ProgressToken,
461    /// Current progress value
462    pub progress: f64,
463    /// Total progress value, if known
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub total: Option<f64>,
466    /// Optional progress message
467    #[serde(skip_serializing_if = "Option::is_none")]
468    pub message: Option<String>,
469}
470
471impl ProgressNotification {
472    /// Create a new progress notification
473    pub fn new(progress_token: ProgressToken, progress: f64) -> Self {
474        Self {
475            method: "notifications/progress".to_string(),
476            params: ProgressParams {
477                progress_token,
478                progress,
479                total: None,
480                message: None,
481            },
482        }
483    }
484
485    /// Create a new progress notification with a total
486    pub fn with_total(progress_token: ProgressToken, progress: f64, total: f64) -> Self {
487        Self {
488            method: "notifications/progress".to_string(),
489            params: ProgressParams {
490                progress_token,
491                progress,
492                total: Some(total),
493                message: None,
494            },
495        }
496    }
497
498    /// Create a new progress notification with a message
499    pub fn with_message(progress_token: ProgressToken, progress: f64, message: impl Into<String>) -> Self {
500        Self {
501            method: "notifications/progress".to_string(),
502            params: ProgressParams {
503                progress_token,
504                progress,
505                total: None,
506                message: Some(message.into()),
507            },
508        }
509    }
510
511    /// Create a new progress notification with a total and message
512    pub fn with_total_and_message(
513        progress_token: ProgressToken,
514        progress: f64,
515        total: f64,
516        message: impl Into<String>,
517    ) -> Self {
518        Self {
519            method: "notifications/progress".to_string(),
520            params: ProgressParams {
521                progress_token,
522                progress,
523                total: Some(total),
524                message: Some(message.into()),
525            },
526        }
527    }
528}