turul_mcp_builders/traits/
notification_traits.rs

1//! Framework traits for MCP notification construction
2//!
3//! **IMPORTANT**: These are framework features, NOT part of the MCP specification.
4
5use turul_mcp_protocol::notifications::{Notification, NotificationParams};
6use serde_json::Value;
7use std::collections::HashMap;
8
9pub trait HasNotificationMetadata {
10    /// The notification method name
11    fn method(&self) -> &str;
12
13    /// Optional notification type or category
14    fn notification_type(&self) -> Option<&str> {
15        None
16    }
17
18    /// Whether this notification requires acknowledgment
19    fn requires_ack(&self) -> bool {
20        false
21    }
22}
23
24/// Trait for notification payload and data structure
25pub trait HasNotificationPayload {
26    /// Get the notification payload data (owned Value for computed serialization)
27    fn payload(&self) -> Option<Value> {
28        None
29    }
30
31    /// Serialize notification to JSON
32    fn serialize_payload(&self) -> Result<String, String> {
33        match self.payload() {
34            Some(data) => {
35                serde_json::to_string(&data).map_err(|e| format!("Serialization error: {}", e))
36            }
37            None => Ok("{}".to_string()),
38        }
39    }
40}
41
42/// Trait for notification delivery rules and filtering
43pub trait HasNotificationRules {
44    /// Optional delivery priority (higher = more important)
45    fn priority(&self) -> u32 {
46        0
47    }
48
49    /// Whether this notification can be batched with others
50    fn can_batch(&self) -> bool {
51        true
52    }
53
54    /// Maximum retry attempts for delivery
55    fn max_retries(&self) -> u32 {
56        3
57    }
58
59    /// Check if notification should be delivered
60    fn should_deliver(&self) -> bool {
61        true
62    }
63}
64
65/// **Complete MCP Notification Creation** - Build real-time event broadcasting systems.
66///
67/// This trait represents a **complete, working MCP notification** that can broadcast
68/// real-time events to connected clients with structured payloads and routing rules.
69/// When you implement the required metadata traits, you automatically get
70/// `NotificationDefinition` for free via blanket implementation.
71///
72/// # What You're Building
73///
74/// A notification is a real-time event system that:
75/// - Broadcasts events to connected clients instantly
76/// - Carries structured JSON payloads with event data
77/// - Supports routing rules for targeted delivery
78/// - Provides reliable event ordering and delivery
79///
80/// # How to Create a Notification
81///
82/// Implement these three traits on your struct:
83///
84/// ```rust
85/// # use turul_mcp_protocol::notifications::*;
86/// # use turul_mcp_builders::prelude::*;
87/// # use serde_json::{Value, json};
88///
89/// // This struct will automatically implement NotificationDefinition!
90/// struct FileChangeNotification {
91///     file_path: String,
92///     change_type: String,
93///     payload_data: Value,
94/// }
95///
96/// impl FileChangeNotification {
97///     fn new(file_path: String, change_type: String) -> Self {
98///         let payload_data = json!({
99///             "path": file_path,
100///             "type": change_type,
101///             "timestamp": "2024-01-01T00:00:00Z"
102///         });
103///         Self { file_path, change_type, payload_data }
104///     }
105/// }
106///
107/// impl HasNotificationMetadata for FileChangeNotification {
108///     fn method(&self) -> &str {
109///         "file/changed"
110///     }
111/// }
112///
113/// impl HasNotificationPayload for FileChangeNotification {
114///     fn payload(&self) -> Option<Value> {
115///         Some(self.payload_data.clone())
116///     }
117/// }
118///
119/// impl HasNotificationRules for FileChangeNotification {
120///     fn priority(&self) -> u32 {
121///         1
122///     }
123/// }
124///
125/// // Now you can use it with the server:
126/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
127/// let notification = FileChangeNotification::new(
128///     "/workspace/src/main.rs".to_string(),
129///     "modified".to_string(),
130/// );
131///
132/// // The notification automatically implements NotificationDefinition
133/// let base_notification = notification.to_notification();
134/// # Ok(())
135/// # }
136/// ```
137///
138/// # Key Benefits
139///
140/// - **Real-Time**: Instant event delivery to connected clients
141/// - **Structured Data**: JSON payloads for rich event information
142/// - **Targeted Delivery**: Client-specific routing rules
143/// - **MCP Compliant**: Fully compatible with MCP 2025-06-18 specification
144///
145/// # Common Use Cases
146///
147/// - File system watch notifications
148/// - Database change events
149/// - User activity broadcasts
150/// - System status updates
151/// - Real-time collaboration events
152pub trait NotificationDefinition:
153    HasNotificationMetadata + HasNotificationPayload + HasNotificationRules
154{
155    /// Convert this notification definition to a base Notification
156    fn to_notification(&self) -> Notification {
157        let mut notification = Notification::new(self.method());
158        if let Some(payload) = self.payload() {
159            let mut params = NotificationParams::new();
160            // Add payload data to params.other
161            if let Ok(obj) = serde_json::from_value::<HashMap<String, Value>>(payload.clone()) {
162                params.other = obj;
163            }
164            notification = notification.with_params(params);
165        }
166        notification
167    }
168
169    /// Validate this notification
170    fn validate(&self) -> Result<(), String> {
171        if self.method().is_empty() {
172            return Err("Notification method cannot be empty".to_string());
173        }
174        if !self.method().starts_with("notifications/") {
175            return Err("Notification method must start with 'notifications/'".to_string());
176        }
177        Ok(())
178    }
179}
180
181// Blanket implementation: any type implementing the fine-grained traits automatically gets NotificationDefinition
182impl<T> NotificationDefinition for T where
183    T: HasNotificationMetadata + HasNotificationPayload + HasNotificationRules
184{
185}