1use crate::meta::Cursor;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct PromptAnnotations {
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub title: Option<String>,
17 }
19
20impl Default for PromptAnnotations {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl PromptAnnotations {
27 pub fn new() -> Self {
28 Self { title: None }
29 }
30
31 pub fn with_title(mut self, title: impl Into<String>) -> Self {
32 self.title = Some(title.into());
33 self
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct Prompt {
41 pub name: String,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub title: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub description: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub arguments: Option<Vec<PromptArgument>>,
52 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
54 pub meta: Option<HashMap<String, Value>>,
55}
56
57impl Prompt {
58 pub fn new(name: impl Into<String>) -> Self {
59 Self {
60 name: name.into(),
61 title: None,
62 description: None,
63 arguments: None,
64 meta: None,
65 }
66 }
67
68 pub fn with_title(mut self, title: impl Into<String>) -> Self {
69 self.title = Some(title.into());
70 self
71 }
72
73 pub fn with_description(mut self, description: impl Into<String>) -> Self {
74 self.description = Some(description.into());
75 self
76 }
77
78 pub fn with_arguments(mut self, arguments: Vec<PromptArgument>) -> Self {
79 self.arguments = Some(arguments);
80 self
81 }
82
83 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
84 self.meta = Some(meta);
85 self
86 }
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
91#[serde(rename_all = "lowercase")]
92pub enum Role {
93 #[serde(rename = "user")]
94 User,
95 #[serde(rename = "assistant")]
96 Assistant,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101#[serde(rename_all = "camelCase")]
102pub struct PromptArgument {
103 pub name: String,
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub title: Option<String>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub description: Option<String>,
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub required: Option<bool>,
114}
115
116impl PromptArgument {
117 pub fn new(name: impl Into<String>) -> Self {
118 Self {
119 name: name.into(),
120 title: None,
121 description: None,
122 required: None,
123 }
124 }
125
126 pub fn with_title(mut self, title: impl Into<String>) -> Self {
127 self.title = Some(title.into());
128 self
129 }
130
131 pub fn with_description(mut self, description: impl Into<String>) -> Self {
132 self.description = Some(description.into());
133 self
134 }
135
136 pub fn required(mut self) -> Self {
137 self.required = Some(true);
138 self
139 }
140
141 pub fn optional(mut self) -> Self {
142 self.required = Some(false);
143 self
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149#[serde(rename_all = "camelCase")]
150pub struct ListPromptsParams {
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub cursor: Option<Cursor>,
154 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
156 pub meta: Option<HashMap<String, Value>>,
157}
158
159impl Default for ListPromptsParams {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165impl ListPromptsParams {
166 pub fn new() -> Self {
167 Self {
168 cursor: None,
169 meta: None,
170 }
171 }
172
173 pub fn with_cursor(mut self, cursor: Cursor) -> Self {
174 self.cursor = Some(cursor);
175 self
176 }
177
178 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
179 self.meta = Some(meta);
180 self
181 }
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
186#[serde(rename_all = "camelCase")]
187pub struct ListPromptsRequest {
188 pub method: String,
190 pub params: ListPromptsParams,
192}
193
194impl Default for ListPromptsRequest {
195 fn default() -> Self {
196 Self::new()
197 }
198}
199
200impl ListPromptsRequest {
201 pub fn new() -> Self {
202 Self {
203 method: "prompts/list".to_string(),
204 params: ListPromptsParams::new(),
205 }
206 }
207
208 pub fn with_cursor(mut self, cursor: Cursor) -> Self {
209 self.params = self.params.with_cursor(cursor);
210 self
211 }
212
213 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
214 self.params = self.params.with_meta(meta);
215 self
216 }
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct ListPromptsResult {
223 pub prompts: Vec<Prompt>,
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub next_cursor: Option<Cursor>,
228 #[serde(
230 default,
231 skip_serializing_if = "Option::is_none",
232 alias = "_meta",
233 rename = "_meta"
234 )]
235 pub meta: Option<HashMap<String, Value>>,
236}
237
238impl ListPromptsResult {
239 pub fn new(prompts: Vec<Prompt>) -> Self {
240 Self {
241 prompts,
242 next_cursor: None,
243 meta: None,
244 }
245 }
246
247 pub fn with_next_cursor(mut self, cursor: Cursor) -> Self {
248 self.next_cursor = Some(cursor);
249 self
250 }
251
252 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
253 self.meta = Some(meta);
254 self
255 }
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase")]
261pub struct GetPromptParams {
262 pub name: String,
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub arguments: Option<HashMap<String, String>>,
267 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
269 pub meta: Option<HashMap<String, Value>>,
270}
271
272impl GetPromptParams {
273 pub fn new(name: impl Into<String>) -> Self {
274 Self {
275 name: name.into(),
276 arguments: None,
277 meta: None,
278 }
279 }
280
281 pub fn with_arguments(mut self, arguments: HashMap<String, String>) -> Self {
282 self.arguments = Some(arguments);
283 self
284 }
285
286 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
287 self.meta = Some(meta);
288 self
289 }
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
294#[serde(rename_all = "camelCase")]
295pub struct GetPromptRequest {
296 pub method: String,
298 pub params: GetPromptParams,
300}
301
302impl GetPromptRequest {
303 pub fn new(name: impl Into<String>) -> Self {
304 Self {
305 method: "prompts/get".to_string(),
306 params: GetPromptParams::new(name),
307 }
308 }
309
310 pub fn with_arguments(mut self, arguments: HashMap<String, String>) -> Self {
311 self.params = self.params.with_arguments(arguments);
312 self
313 }
314
315 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
316 self.params = self.params.with_meta(meta);
317 self
318 }
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
323#[serde(rename_all = "camelCase")]
324pub struct PromptMessage {
325 pub role: Role,
327 pub content: ContentBlock,
329}
330
331pub use crate::content::{ContentBlock, ResourceContents, ResourceReference};
333
334impl PromptMessage {
335 pub fn user_text(content: impl Into<String>) -> Self {
336 Self {
337 role: Role::User,
338 content: ContentBlock::text(content),
339 }
340 }
341
342 pub fn assistant_text(content: impl Into<String>) -> Self {
343 Self {
344 role: Role::Assistant,
345 content: ContentBlock::text(content),
346 }
347 }
348
349 pub fn user_image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
350 Self {
351 role: Role::User,
352 content: ContentBlock::image(data, mime_type),
353 }
354 }
355
356 pub fn text(content: impl Into<String>) -> Self {
357 Self::user_text(content)
359 }
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364#[serde(rename_all = "camelCase")]
365pub struct GetPromptResult {
366 #[serde(skip_serializing_if = "Option::is_none")]
368 pub description: Option<String>,
369 pub messages: Vec<PromptMessage>,
371 #[serde(
373 default,
374 skip_serializing_if = "Option::is_none",
375 alias = "_meta",
376 rename = "_meta"
377 )]
378 pub meta: Option<HashMap<String, Value>>,
379}
380
381impl GetPromptResult {
382 pub fn new(messages: Vec<PromptMessage>) -> Self {
383 Self {
384 description: None,
385 messages,
386 meta: None,
387 }
388 }
389
390 pub fn with_description(mut self, description: impl Into<String>) -> Self {
391 self.description = Some(description.into());
392 self
393 }
394
395 pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
396 self.meta = Some(meta);
397 self
398 }
399}
400
401use crate::traits::*;
404
405impl Params for ListPromptsParams {}
407
408impl HasListPromptsParams for ListPromptsParams {
409 fn cursor(&self) -> Option<&Cursor> {
410 self.cursor.as_ref()
411 }
412}
413
414impl HasMetaParam for ListPromptsParams {
415 fn meta(&self) -> Option<&HashMap<String, Value>> {
416 self.meta.as_ref()
417 }
418}
419
420impl HasMethod for ListPromptsRequest {
422 fn method(&self) -> &str {
423 &self.method
424 }
425}
426
427impl HasParams for ListPromptsRequest {
428 fn params(&self) -> Option<&dyn Params> {
429 Some(&self.params)
430 }
431}
432
433impl HasData for ListPromptsResult {
435 fn data(&self) -> HashMap<String, Value> {
436 let mut data = HashMap::new();
437 data.insert(
438 "prompts".to_string(),
439 serde_json::to_value(&self.prompts).unwrap_or(Value::Null),
440 );
441 if let Some(ref next_cursor) = self.next_cursor {
442 data.insert(
443 "nextCursor".to_string(),
444 Value::String(next_cursor.as_str().to_string()),
445 );
446 }
447 data
448 }
449}
450
451impl HasMeta for ListPromptsResult {
452 fn meta(&self) -> Option<HashMap<String, Value>> {
453 self.meta.clone()
454 }
455}
456
457impl RpcResult for ListPromptsResult {}
458
459impl crate::traits::ListPromptsResult for ListPromptsResult {
460 fn prompts(&self) -> &Vec<Prompt> {
461 &self.prompts
462 }
463
464 fn next_cursor(&self) -> Option<&Cursor> {
465 self.next_cursor.as_ref()
466 }
467}
468
469impl Params for GetPromptParams {}
471
472impl HasGetPromptParams for GetPromptParams {
473 fn name(&self) -> &String {
474 &self.name
475 }
476
477 fn arguments(&self) -> Option<&HashMap<String, String>> {
478 self.arguments.as_ref()
479 }
480}
481
482impl HasMetaParam for GetPromptParams {
483 fn meta(&self) -> Option<&HashMap<String, Value>> {
484 self.meta.as_ref()
485 }
486}
487
488impl HasMethod for GetPromptRequest {
490 fn method(&self) -> &str {
491 &self.method
492 }
493}
494
495impl HasParams for GetPromptRequest {
496 fn params(&self) -> Option<&dyn Params> {
497 Some(&self.params)
498 }
499}
500
501impl HasData for GetPromptResult {
503 fn data(&self) -> HashMap<String, Value> {
504 let mut data = HashMap::new();
505 data.insert(
506 "messages".to_string(),
507 serde_json::to_value(&self.messages).unwrap_or(Value::Null),
508 );
509 if let Some(ref description) = self.description {
510 data.insert(
511 "description".to_string(),
512 Value::String(description.clone()),
513 );
514 }
515 data
516 }
517}
518
519impl HasMeta for GetPromptResult {
520 fn meta(&self) -> Option<HashMap<String, Value>> {
521 self.meta.clone()
522 }
523}
524
525impl RpcResult for GetPromptResult {}
526
527impl crate::traits::GetPromptResult for GetPromptResult {
528 fn description(&self) -> Option<&String> {
529 self.description.as_ref()
530 }
531
532 fn messages(&self) -> &Vec<PromptMessage> {
533 &self.messages
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 #[test]
542 fn test_prompt_creation() {
543 let arg = PromptArgument::new("topic")
544 .with_description("The topic to write about")
545 .required();
546
547 let prompt = Prompt::new("write_essay")
548 .with_description("Write an essay about a topic")
549 .with_arguments(vec![arg]);
550
551 assert_eq!(prompt.name, "write_essay");
552 assert!(prompt.description.is_some());
553 assert!(prompt.arguments.is_some());
554 }
555
556 #[test]
557 fn test_prompt_message() {
558 let text_msg = PromptMessage::text("Hello, world!");
559 let user_image_msg = PromptMessage::user_image("base64data", "image/png");
560 let assistant_text_msg = PromptMessage::assistant_text("Response text");
561
562 assert_eq!(text_msg.role, Role::User);
564 assert!(matches!(text_msg.content, ContentBlock::Text { .. }));
565
566 assert_eq!(user_image_msg.role, Role::User);
567 assert!(matches!(user_image_msg.content, ContentBlock::Image { .. }));
568
569 assert_eq!(assistant_text_msg.role, Role::Assistant);
570 assert!(matches!(
571 assistant_text_msg.content,
572 ContentBlock::Text { .. }
573 ));
574 }
575
576 #[test]
577 fn test_get_prompt_request() {
578 let mut args = HashMap::new();
579 args.insert("topic".to_string(), "AI Safety".to_string()); let request = GetPromptRequest::new("write_essay").with_arguments(args);
582
583 assert_eq!(request.params.name, "write_essay");
584 assert!(request.params.arguments.is_some());
585
586 if let Some(ref arguments) = request.params.arguments {
588 assert_eq!(arguments.get("topic"), Some(&"AI Safety".to_string()));
589 }
590 }
591
592 #[test]
593 fn test_get_prompt_response() {
594 let messages = vec![
595 PromptMessage::user_text("Write an essay about: "),
596 PromptMessage::assistant_text("AI Safety"),
597 ];
598
599 let response = GetPromptResult::new(messages).with_description("Generated essay prompt");
600
601 assert_eq!(response.messages.len(), 2);
602 assert!(response.description.is_some());
603
604 assert_eq!(response.messages[0].role, Role::User);
606 assert_eq!(response.messages[1].role, Role::Assistant);
607 }
608
609 #[test]
610 fn test_serialization() {
611 let prompt = Prompt::new("test_prompt").with_description("A test prompt");
612
613 let json = serde_json::to_string(&prompt).unwrap();
614 assert!(json.contains("test_prompt"));
615 assert!(json.contains("A test prompt"));
616
617 let parsed: Prompt = serde_json::from_str(&json).unwrap();
618 assert_eq!(parsed.name, "test_prompt");
619 }
620}