1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
11#[serde(rename_all = "camelCase")]
12pub enum ElicitationAction {
13    Accept,
15    Decline,
17    Cancel,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct ElicitationSchema {
24    #[serde(rename = "type")]
26    pub schema_type: String,
27    pub properties: HashMap<String, PrimitiveSchemaDefinition>,
29    #[serde(skip_serializing_if = "Option::is_none")]
31    pub required: Option<Vec<String>>,
32    #[serde(
34        rename = "additionalProperties",
35        skip_serializing_if = "Option::is_none"
36    )]
37    pub additional_properties: Option<bool>,
38}
39
40impl ElicitationSchema {
41    pub fn new() -> Self {
43        Self {
44            schema_type: "object".to_string(),
45            properties: HashMap::new(),
46            required: Some(Vec::new()),
47            additional_properties: Some(false),
48        }
49    }
50
51    pub fn add_string_property(
53        mut self,
54        name: String,
55        required: bool,
56        description: Option<String>,
57    ) -> Self {
58        let property = PrimitiveSchemaDefinition::String {
59            title: None,
60            description,
61            format: None,
62            min_length: None,
63            max_length: None,
64            enum_values: None,
65            enum_names: None,
66        };
67
68        self.properties.insert(name.clone(), property);
69
70        if required && let Some(ref mut required_fields) = self.required {
71            required_fields.push(name);
72        }
73
74        self
75    }
76
77    pub fn add_number_property(
79        mut self,
80        name: String,
81        required: bool,
82        description: Option<String>,
83        minimum: Option<f64>,
84        maximum: Option<f64>,
85    ) -> Self {
86        let property = PrimitiveSchemaDefinition::Number {
87            title: None,
88            description,
89            minimum,
90            maximum,
91        };
92
93        self.properties.insert(name.clone(), property);
94
95        if required && let Some(ref mut required_fields) = self.required {
96            required_fields.push(name);
97        }
98
99        self
100    }
101
102    pub fn add_boolean_property(
104        mut self,
105        name: String,
106        required: bool,
107        description: Option<String>,
108        default: Option<bool>,
109    ) -> Self {
110        let property = PrimitiveSchemaDefinition::Boolean {
111            title: None,
112            description,
113            default,
114        };
115
116        self.properties.insert(name.clone(), property);
117
118        if required && let Some(ref mut required_fields) = self.required {
119            required_fields.push(name);
120        }
121
122        self
123    }
124}
125
126impl Default for ElicitationSchema {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134#[serde(tag = "type")]
135pub enum PrimitiveSchemaDefinition {
136    #[serde(rename = "string")]
138    String {
139        #[serde(skip_serializing_if = "Option::is_none")]
141        title: Option<String>,
142        #[serde(skip_serializing_if = "Option::is_none")]
144        description: Option<String>,
145        #[serde(skip_serializing_if = "Option::is_none")]
147        format: Option<String>,
148        #[serde(skip_serializing_if = "Option::is_none")]
150        #[serde(rename = "minLength")]
151        min_length: Option<u32>,
152        #[serde(skip_serializing_if = "Option::is_none")]
154        #[serde(rename = "maxLength")]
155        max_length: Option<u32>,
156        #[serde(skip_serializing_if = "Option::is_none")]
158        #[serde(rename = "enum")]
159        enum_values: Option<Vec<String>>,
160        #[serde(skip_serializing_if = "Option::is_none")]
162        #[serde(rename = "enumNames")]
163        enum_names: Option<Vec<String>>,
164    },
165    #[serde(rename = "number")]
167    Number {
168        #[serde(skip_serializing_if = "Option::is_none")]
170        title: Option<String>,
171        #[serde(skip_serializing_if = "Option::is_none")]
173        description: Option<String>,
174        #[serde(skip_serializing_if = "Option::is_none")]
176        minimum: Option<f64>,
177        #[serde(skip_serializing_if = "Option::is_none")]
179        maximum: Option<f64>,
180    },
181    #[serde(rename = "integer")]
183    Integer {
184        #[serde(skip_serializing_if = "Option::is_none")]
186        title: Option<String>,
187        #[serde(skip_serializing_if = "Option::is_none")]
189        description: Option<String>,
190        #[serde(skip_serializing_if = "Option::is_none")]
192        minimum: Option<i64>,
193        #[serde(skip_serializing_if = "Option::is_none")]
195        maximum: Option<i64>,
196    },
197    #[serde(rename = "boolean")]
199    Boolean {
200        #[serde(skip_serializing_if = "Option::is_none")]
202        title: Option<String>,
203        #[serde(skip_serializing_if = "Option::is_none")]
205        description: Option<String>,
206        #[serde(skip_serializing_if = "Option::is_none")]
208        default: Option<bool>,
209    },
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct ElicitRequestParams {
215    pub message: String,
217    #[serde(rename = "requestedSchema")]
219    pub schema: ElicitationSchema,
220    #[serde(rename = "timeoutMs", skip_serializing_if = "Option::is_none")]
222    pub timeout_ms: Option<u32>,
223    #[serde(skip_serializing_if = "Option::is_none")]
225    pub cancellable: Option<bool>,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct ElicitRequest {
231    #[serde(flatten)]
233    pub params: ElicitRequestParams,
234    #[serde(skip_serializing_if = "Option::is_none")]
236    pub _meta: Option<serde_json::Value>,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
241pub struct ElicitResult {
242    pub action: ElicitationAction,
244    #[serde(skip_serializing_if = "Option::is_none")]
246    pub content: Option<std::collections::HashMap<String, serde_json::Value>>,
247    #[serde(skip_serializing_if = "Option::is_none")]
249    pub _meta: Option<serde_json::Value>,
250}