turul_mcp_builders/traits/
elicitation_traits.rs

1//! Framework traits for MCP elicitation construction
2//!
3//! **IMPORTANT**: These are framework features, NOT part of the MCP specification.
4
5use turul_mcp_protocol::elicitation::{ElicitCreateRequest, ElicitationSchema};
6use serde_json::Value;
7use std::collections::HashMap;
8
9pub trait HasElicitationMetadata {
10    /// The message to present to the user
11    fn message(&self) -> &str;
12
13    /// Optional title for the elicitation dialog
14    fn title(&self) -> Option<&str> {
15        None
16    }
17}
18
19/// Trait for elicitation schema definition (restricted to primitive types per MCP spec)
20pub trait HasElicitationSchema {
21    /// Restricted schema defining structure of input to collect (primitives only)
22    fn requested_schema(&self) -> &ElicitationSchema;
23
24    /// Validate that schema only contains primitive types (per MCP spec)
25    fn validate_schema(&self) -> Result<(), String> {
26        // All schemas in ElicitationSchema are already primitive-only by design
27        Ok(())
28    }
29}
30
31/// Trait for elicitation validation and handling
32pub trait HasElicitationHandling {
33    /// Validate submitted content against the schema
34    fn validate_content(&self, _content: &HashMap<String, Value>) -> Result<(), String> {
35        // Basic validation - can be extended
36        Ok(())
37    }
38
39    /// Process accepted content (transform, normalize, etc.)
40    fn process_content(
41        &self,
42        content: HashMap<String, Value>,
43    ) -> Result<HashMap<String, Value>, String> {
44        Ok(content)
45    }
46}
47
48/// **Complete MCP Elicitation Creation** - Build schema-validated user input collection systems.
49///
50/// This trait represents a **complete, working MCP elicitation** that can prompt users
51/// for structured input and validate their responses against JSON schemas. When you implement
52/// the required metadata traits, you automatically get `ElicitationDefinition` for free
53/// via blanket implementation.
54///
55/// # What You're Building
56///
57/// An elicitation is a sophisticated user input system that:
58/// - Presents a clear message/prompt to the user
59/// - Defines a JSON schema for expected input structure
60/// - Validates user responses against that schema
61/// - Processes the validated data for your application
62///
63/// # How to Create an Elicitation
64///
65/// Implement these three traits on your struct:
66///
67/// ```rust
68/// # use turul_mcp_protocol::elicitation::*;
69/// # use turul_mcp_builders::prelude::*;
70/// # use serde_json::Value;
71/// # use std::collections::HashMap;
72///
73/// // This struct will automatically implement ElicitationDefinition!
74/// struct UserPreferencesForm {
75///     context: String,
76///     schema: ElicitationSchema,
77/// }
78///
79/// impl UserPreferencesForm {
80///     fn new(context: String) -> Self {
81///         let mut properties = HashMap::new();
82///         properties.insert("theme".to_string(), PrimitiveSchemaDefinition::Enum(EnumSchema {
83///             schema_type: "string".to_string(),
84///             title: None,
85///             description: Some("UI theme preference".to_string()),
86///             enum_values: vec!["dark".to_string(), "light".to_string()],
87///             enum_names: None,
88///         }));
89///         properties.insert("notifications".to_string(), PrimitiveSchemaDefinition::Boolean(BooleanSchema {
90///             schema_type: "boolean".to_string(),
91///             title: None,
92///             description: Some("Enable notifications".to_string()),
93///             default: Some(true),
94///         }));
95///         properties.insert("max_items".to_string(), PrimitiveSchemaDefinition::Number(NumberSchema {
96///             schema_type: "number".to_string(),
97///             title: None,
98///             description: Some("Maximum items to display".to_string()),
99///             minimum: Some(1.0),
100///             maximum: Some(100.0),
101///         }));
102///
103///         let schema = ElicitationSchema {
104///             schema_type: "object".to_string(),
105///             properties,
106///             required: Some(vec!["theme".to_string()]),
107///         };
108///
109///         Self { context, schema }
110///     }
111/// }
112///
113/// impl HasElicitationMetadata for UserPreferencesForm {
114///     fn message(&self) -> &str {
115///         "Please configure your preferences for this project"
116///     }
117/// }
118///
119/// impl HasElicitationSchema for UserPreferencesForm {
120///     fn requested_schema(&self) -> &ElicitationSchema {
121///         &self.schema
122///     }
123/// }
124///
125/// impl HasElicitationHandling for UserPreferencesForm {
126///     fn process_content(&self, content: HashMap<String, Value>) -> Result<HashMap<String, Value>, String> {
127///         // Validate that theme is acceptable
128///         if let Some(theme) = content.get("theme") {
129///             if !["dark", "light"].contains(&theme.as_str().unwrap_or("")) {
130///                 return Err("Theme must be 'dark' or 'light'".to_string());
131///             }
132///         }
133///
134///         // Process and potentially transform the data
135///         let mut processed = content.clone();
136///         processed.insert("processed_at".to_string(), Value::String("2024-01-01T00:00:00Z".to_string()));
137///         Ok(processed)
138///     }
139/// }
140///
141/// // Now you can use it with the server:
142/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
143/// let form = UserPreferencesForm::new("project-setup".to_string());
144///
145/// // The elicitation automatically implements ElicitationDefinition
146/// let create_request = form.to_create_request();
147/// # Ok(())
148/// # }
149/// ```
150///
151/// # Key Benefits
152///
153/// - **Type Safety**: Schema validation happens at the protocol level
154/// - **Automatic Implementation**: Just implement the three component traits
155/// - **Flexible Processing**: Handle and transform user input as needed
156/// - **MCP Compliant**: Fully compatible with MCP 2025-06-18 specification
157///
158/// # Common Use Cases
159///
160/// - Configuration forms with validation
161/// - User preference collection
162/// - Survey and feedback systems
163/// - Structured data entry workflows
164/// - Multi-step input wizards
165pub trait ElicitationDefinition:
166    HasElicitationMetadata + HasElicitationSchema + HasElicitationHandling
167{
168    /// Convert this elicitation definition to a protocol ElicitCreateRequest
169    fn to_create_request(&self) -> ElicitCreateRequest {
170        ElicitCreateRequest::new(self.message(), self.requested_schema().clone())
171    }
172}
173
174// Blanket implementation: any type implementing the fine-grained traits automatically gets ElicitationDefinition
175impl<T> ElicitationDefinition for T where
176    T: HasElicitationMetadata + HasElicitationSchema + HasElicitationHandling
177{
178}