1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9use crate::error::JsonRpcError;
10
11pub const JSONRPC_VERSION: &str = "2.0";
13
14pub const LATEST_PROTOCOL_VERSION: &str = "2025-11-25";
16
17pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &["2025-11-25", "2025-03-26"];
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct JsonRpcRequest {
23 pub jsonrpc: String,
25 pub id: RequestId,
27 pub method: String,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub params: Option<Value>,
32}
33
34impl JsonRpcRequest {
35 pub fn new(id: impl Into<RequestId>, method: impl Into<String>) -> Self {
37 Self {
38 jsonrpc: JSONRPC_VERSION.to_string(),
39 id: id.into(),
40 method: method.into(),
41 params: None,
42 }
43 }
44
45 pub fn with_params(mut self, params: Value) -> Self {
47 self.params = Some(params);
48 self
49 }
50
51 pub fn validate(&self) -> Result<(), JsonRpcError> {
54 if self.jsonrpc != JSONRPC_VERSION {
55 return Err(JsonRpcError::invalid_request(format!(
56 "Invalid JSON-RPC version: expected '{}', got '{}'",
57 JSONRPC_VERSION, self.jsonrpc
58 )));
59 }
60 Ok(())
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct JsonRpcResultResponse {
67 pub jsonrpc: String,
69 pub id: RequestId,
71 pub result: Value,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct JsonRpcErrorResponse {
78 pub jsonrpc: String,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub id: Option<RequestId>,
83 pub error: JsonRpcError,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89#[serde(untagged)]
90pub enum JsonRpcResponse {
91 Result(JsonRpcResultResponse),
93 Error(JsonRpcErrorResponse),
95}
96
97impl JsonRpcResponse {
98 pub fn result(id: RequestId, result: Value) -> Self {
100 Self::Result(JsonRpcResultResponse {
101 jsonrpc: JSONRPC_VERSION.to_string(),
102 id,
103 result,
104 })
105 }
106
107 pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
109 Self::Error(JsonRpcErrorResponse {
110 jsonrpc: JSONRPC_VERSION.to_string(),
111 id,
112 error,
113 })
114 }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119#[serde(untagged)]
120pub enum JsonRpcMessage {
121 Single(JsonRpcRequest),
123 Batch(Vec<JsonRpcRequest>),
125}
126
127impl JsonRpcMessage {
128 pub fn is_batch(&self) -> bool {
130 matches!(self, JsonRpcMessage::Batch(_))
131 }
132
133 pub fn len(&self) -> usize {
135 match self {
136 JsonRpcMessage::Single(_) => 1,
137 JsonRpcMessage::Batch(batch) => batch.len(),
138 }
139 }
140
141 pub fn is_empty(&self) -> bool {
143 self.len() == 0
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149#[serde(untagged)]
150pub enum JsonRpcResponseMessage {
151 Single(JsonRpcResponse),
153 Batch(Vec<JsonRpcResponse>),
155}
156
157impl JsonRpcResponseMessage {
158 pub fn is_batch(&self) -> bool {
160 matches!(self, JsonRpcResponseMessage::Batch(_))
161 }
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct JsonRpcNotification {
167 pub jsonrpc: String,
168 pub method: String,
169 #[serde(default, skip_serializing_if = "Option::is_none")]
170 pub params: Option<Value>,
171}
172
173impl JsonRpcNotification {
174 pub fn new(method: impl Into<String>) -> Self {
175 Self {
176 jsonrpc: JSONRPC_VERSION.to_string(),
177 method: method.into(),
178 params: None,
179 }
180 }
181
182 pub fn with_params(mut self, params: Value) -> Self {
183 self.params = Some(params);
184 self
185 }
186}
187
188pub mod notifications {
190 pub const INITIALIZED: &str = "notifications/initialized";
192 pub const CANCELLED: &str = "notifications/cancelled";
194 pub const PROGRESS: &str = "notifications/progress";
196 pub const TOOLS_LIST_CHANGED: &str = "notifications/tools/list_changed";
198 pub const RESOURCES_LIST_CHANGED: &str = "notifications/resources/list_changed";
200 pub const RESOURCE_UPDATED: &str = "notifications/resources/updated";
202 pub const PROMPTS_LIST_CHANGED: &str = "notifications/prompts/list_changed";
204 pub const ROOTS_LIST_CHANGED: &str = "notifications/roots/list_changed";
206 pub const MESSAGE: &str = "notifications/message";
208 pub const TASK_STATUS_CHANGED: &str = "notifications/tasks/status_changed";
210 pub const ELICITATION_COMPLETE: &str = "notifications/elicitation/complete";
212}
213
214#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
218#[serde(rename_all = "lowercase")]
219pub enum LogLevel {
220 Emergency,
222 Alert,
224 Critical,
226 Error,
228 Warning,
230 Notice,
232 #[default]
234 Info,
235 Debug,
237}
238
239impl std::fmt::Display for LogLevel {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 match self {
242 LogLevel::Emergency => write!(f, "emergency"),
243 LogLevel::Alert => write!(f, "alert"),
244 LogLevel::Critical => write!(f, "critical"),
245 LogLevel::Error => write!(f, "error"),
246 LogLevel::Warning => write!(f, "warning"),
247 LogLevel::Notice => write!(f, "notice"),
248 LogLevel::Info => write!(f, "info"),
249 LogLevel::Debug => write!(f, "debug"),
250 }
251 }
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct LoggingMessageParams {
257 pub level: LogLevel,
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub logger: Option<String>,
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub data: Option<Value>,
265}
266
267impl LoggingMessageParams {
268 pub fn new(level: LogLevel) -> Self {
270 Self {
271 level,
272 logger: None,
273 data: None,
274 }
275 }
276
277 pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
279 self.logger = Some(logger.into());
280 self
281 }
282
283 pub fn with_data(mut self, data: Value) -> Self {
285 self.data = Some(data);
286 self
287 }
288}
289
290#[derive(Debug, Clone, Deserialize)]
292pub struct SetLogLevelParams {
293 pub level: LogLevel,
295}
296
297#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
299#[serde(untagged)]
300pub enum RequestId {
301 String(String),
302 Number(i64),
303}
304
305impl From<String> for RequestId {
306 fn from(s: String) -> Self {
307 RequestId::String(s)
308 }
309}
310
311impl From<&str> for RequestId {
312 fn from(s: &str) -> Self {
313 RequestId::String(s.to_string())
314 }
315}
316
317impl From<i64> for RequestId {
318 fn from(n: i64) -> Self {
319 RequestId::Number(n)
320 }
321}
322
323impl From<i32> for RequestId {
324 fn from(n: i32) -> Self {
325 RequestId::Number(n as i64)
326 }
327}
328
329#[derive(Debug, Clone)]
335pub enum McpRequest {
336 Initialize(InitializeParams),
338 ListTools(ListToolsParams),
340 CallTool(CallToolParams),
342 ListResources(ListResourcesParams),
344 ListResourceTemplates(ListResourceTemplatesParams),
346 ReadResource(ReadResourceParams),
348 SubscribeResource(SubscribeResourceParams),
350 UnsubscribeResource(UnsubscribeResourceParams),
352 ListPrompts(ListPromptsParams),
354 GetPrompt(GetPromptParams),
356 EnqueueTask(EnqueueTaskParams),
358 ListTasks(ListTasksParams),
360 GetTaskInfo(GetTaskInfoParams),
362 GetTaskResult(GetTaskResultParams),
364 CancelTask(CancelTaskParams),
366 Ping,
368 SetLoggingLevel(SetLogLevelParams),
370 Complete(CompleteParams),
372 Unknown {
374 method: String,
375 params: Option<Value>,
376 },
377}
378
379impl McpRequest {
380 pub fn method_name(&self) -> &str {
382 match self {
383 McpRequest::Initialize(_) => "initialize",
384 McpRequest::ListTools(_) => "tools/list",
385 McpRequest::CallTool(_) => "tools/call",
386 McpRequest::ListResources(_) => "resources/list",
387 McpRequest::ListResourceTemplates(_) => "resources/templates/list",
388 McpRequest::ReadResource(_) => "resources/read",
389 McpRequest::SubscribeResource(_) => "resources/subscribe",
390 McpRequest::UnsubscribeResource(_) => "resources/unsubscribe",
391 McpRequest::ListPrompts(_) => "prompts/list",
392 McpRequest::GetPrompt(_) => "prompts/get",
393 McpRequest::EnqueueTask(_) => "tasks/enqueue",
394 McpRequest::ListTasks(_) => "tasks/list",
395 McpRequest::GetTaskInfo(_) => "tasks/get",
396 McpRequest::GetTaskResult(_) => "tasks/result",
397 McpRequest::CancelTask(_) => "tasks/cancel",
398 McpRequest::Ping => "ping",
399 McpRequest::SetLoggingLevel(_) => "logging/setLevel",
400 McpRequest::Complete(_) => "completion/complete",
401 McpRequest::Unknown { method, .. } => method,
402 }
403 }
404}
405
406#[derive(Debug, Clone)]
408pub enum McpNotification {
409 Initialized,
411 Cancelled(CancelledParams),
413 Progress(ProgressParams),
415 RootsListChanged,
417 Unknown {
419 method: String,
420 params: Option<Value>,
421 },
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
426#[serde(rename_all = "camelCase")]
427pub struct CancelledParams {
428 pub request_id: RequestId,
430 #[serde(skip_serializing_if = "Option::is_none")]
432 pub reason: Option<String>,
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
437#[serde(rename_all = "camelCase")]
438pub struct ProgressParams {
439 pub progress_token: ProgressToken,
441 pub progress: f64,
443 #[serde(skip_serializing_if = "Option::is_none")]
445 pub total: Option<f64>,
446 #[serde(skip_serializing_if = "Option::is_none")]
448 pub message: Option<String>,
449}
450
451#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
453#[serde(untagged)]
454pub enum ProgressToken {
455 String(String),
456 Number(i64),
457}
458
459#[derive(Debug, Clone, Default, Serialize, Deserialize)]
461#[serde(rename_all = "camelCase")]
462pub struct RequestMeta {
463 #[serde(skip_serializing_if = "Option::is_none")]
465 pub progress_token: Option<ProgressToken>,
466}
467
468#[derive(Debug, Clone, Serialize)]
470#[serde(untagged)]
471pub enum McpResponse {
472 Initialize(InitializeResult),
473 ListTools(ListToolsResult),
474 CallTool(CallToolResult),
475 ListResources(ListResourcesResult),
476 ListResourceTemplates(ListResourceTemplatesResult),
477 ReadResource(ReadResourceResult),
478 SubscribeResource(EmptyResult),
479 UnsubscribeResource(EmptyResult),
480 ListPrompts(ListPromptsResult),
481 GetPrompt(GetPromptResult),
482 EnqueueTask(EnqueueTaskResult),
483 ListTasks(ListTasksResult),
484 GetTaskInfo(TaskInfo),
485 GetTaskResult(GetTaskResultResult),
486 CancelTask(CancelTaskResult),
487 SetLoggingLevel(EmptyResult),
488 Complete(CompleteResult),
489 Pong(EmptyResult),
490 Empty(EmptyResult),
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
498#[serde(rename_all = "camelCase")]
499pub struct InitializeParams {
500 pub protocol_version: String,
501 pub capabilities: ClientCapabilities,
502 pub client_info: Implementation,
503}
504
505#[derive(Debug, Clone, Default, Deserialize, Serialize)]
506pub struct ClientCapabilities {
507 #[serde(default, skip_serializing_if = "Option::is_none")]
508 pub roots: Option<RootsCapability>,
509 #[serde(default, skip_serializing_if = "Option::is_none")]
510 pub sampling: Option<SamplingCapability>,
511 #[serde(default, skip_serializing_if = "Option::is_none")]
512 pub elicitation: Option<ElicitationCapability>,
513}
514
515#[derive(Debug, Clone, Default, Deserialize, Serialize)]
517pub struct ElicitationCapability {
518 #[serde(default, skip_serializing_if = "Option::is_none")]
520 pub form: Option<ElicitationFormCapability>,
521 #[serde(default, skip_serializing_if = "Option::is_none")]
523 pub url: Option<ElicitationUrlCapability>,
524}
525
526#[derive(Debug, Clone, Default, Deserialize, Serialize)]
528pub struct ElicitationFormCapability {}
529
530#[derive(Debug, Clone, Default, Deserialize, Serialize)]
532pub struct ElicitationUrlCapability {}
533
534#[derive(Debug, Clone, Default, Deserialize, Serialize)]
536#[serde(rename_all = "camelCase")]
537pub struct RootsCapability {
538 #[serde(default)]
540 pub list_changed: bool,
541}
542
543#[derive(Debug, Clone, Deserialize, Serialize)]
550pub struct Root {
551 pub uri: String,
553 #[serde(default, skip_serializing_if = "Option::is_none")]
555 pub name: Option<String>,
556}
557
558impl Root {
559 pub fn new(uri: impl Into<String>) -> Self {
561 Self {
562 uri: uri.into(),
563 name: None,
564 }
565 }
566
567 pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
569 Self {
570 uri: uri.into(),
571 name: Some(name.into()),
572 }
573 }
574}
575
576#[derive(Debug, Clone, Default, Deserialize, Serialize)]
578pub struct ListRootsParams {
579 #[serde(default, rename = "_meta", skip_serializing_if = "Option::is_none")]
581 pub meta: Option<RequestMeta>,
582}
583
584#[derive(Debug, Clone, Deserialize, Serialize)]
586pub struct ListRootsResult {
587 pub roots: Vec<Root>,
589}
590
591#[derive(Debug, Clone, Default, Deserialize, Serialize)]
592pub struct SamplingCapability {}
593
594#[derive(Debug, Clone, Default, Deserialize, Serialize)]
596pub struct CompletionsCapability {}
597
598#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct PromptReference {
605 #[serde(rename = "type")]
607 pub ref_type: String,
608 pub name: String,
610}
611
612impl PromptReference {
613 pub fn new(name: impl Into<String>) -> Self {
615 Self {
616 ref_type: "ref/prompt".to_string(),
617 name: name.into(),
618 }
619 }
620}
621
622#[derive(Debug, Clone, Serialize, Deserialize)]
624pub struct ResourceReference {
625 #[serde(rename = "type")]
627 pub ref_type: String,
628 pub uri: String,
630}
631
632impl ResourceReference {
633 pub fn new(uri: impl Into<String>) -> Self {
635 Self {
636 ref_type: "ref/resource".to_string(),
637 uri: uri.into(),
638 }
639 }
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize)]
644#[serde(tag = "type")]
645pub enum CompletionReference {
646 #[serde(rename = "ref/prompt")]
648 Prompt {
649 name: String,
651 },
652 #[serde(rename = "ref/resource")]
654 Resource {
655 uri: String,
657 },
658}
659
660impl CompletionReference {
661 pub fn prompt(name: impl Into<String>) -> Self {
663 Self::Prompt { name: name.into() }
664 }
665
666 pub fn resource(uri: impl Into<String>) -> Self {
668 Self::Resource { uri: uri.into() }
669 }
670}
671
672#[derive(Debug, Clone, Serialize, Deserialize)]
674pub struct CompletionArgument {
675 pub name: String,
677 pub value: String,
679}
680
681impl CompletionArgument {
682 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
684 Self {
685 name: name.into(),
686 value: value.into(),
687 }
688 }
689}
690
691#[derive(Debug, Clone, Serialize, Deserialize)]
693#[serde(rename_all = "camelCase")]
694pub struct CompleteParams {
695 #[serde(rename = "ref")]
697 pub reference: CompletionReference,
698 pub argument: CompletionArgument,
700}
701
702#[derive(Debug, Clone, Serialize, Deserialize)]
704#[serde(rename_all = "camelCase")]
705pub struct Completion {
706 pub values: Vec<String>,
708 #[serde(default, skip_serializing_if = "Option::is_none")]
710 pub total: Option<u32>,
711 #[serde(default, skip_serializing_if = "Option::is_none")]
713 pub has_more: Option<bool>,
714}
715
716impl Completion {
717 pub fn new(values: Vec<String>) -> Self {
719 Self {
720 values,
721 total: None,
722 has_more: None,
723 }
724 }
725
726 pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
728 Self {
729 values,
730 total: Some(total),
731 has_more: Some(has_more),
732 }
733 }
734}
735
736#[derive(Debug, Clone, Serialize, Deserialize)]
738pub struct CompleteResult {
739 pub completion: Completion,
741}
742
743impl CompleteResult {
744 pub fn new(values: Vec<String>) -> Self {
746 Self {
747 completion: Completion::new(values),
748 }
749 }
750
751 pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
753 Self {
754 completion: Completion::with_pagination(values, total, has_more),
755 }
756 }
757}
758
759#[derive(Debug, Clone, Serialize, Deserialize)]
765pub struct ModelHint {
766 pub name: String,
768}
769
770impl ModelHint {
771 pub fn new(name: impl Into<String>) -> Self {
773 Self { name: name.into() }
774 }
775}
776
777#[derive(Debug, Clone, Default, Serialize, Deserialize)]
779#[serde(rename_all = "camelCase")]
780pub struct ModelPreferences {
781 #[serde(default, skip_serializing_if = "Option::is_none")]
783 pub speed_priority: Option<f64>,
784 #[serde(default, skip_serializing_if = "Option::is_none")]
786 pub intelligence_priority: Option<f64>,
787 #[serde(default, skip_serializing_if = "Option::is_none")]
789 pub cost_priority: Option<f64>,
790 #[serde(default, skip_serializing_if = "Vec::is_empty")]
792 pub hints: Vec<ModelHint>,
793}
794
795impl ModelPreferences {
796 pub fn new() -> Self {
798 Self::default()
799 }
800
801 pub fn speed(mut self, priority: f64) -> Self {
803 self.speed_priority = Some(priority.clamp(0.0, 1.0));
804 self
805 }
806
807 pub fn intelligence(mut self, priority: f64) -> Self {
809 self.intelligence_priority = Some(priority.clamp(0.0, 1.0));
810 self
811 }
812
813 pub fn cost(mut self, priority: f64) -> Self {
815 self.cost_priority = Some(priority.clamp(0.0, 1.0));
816 self
817 }
818
819 pub fn hint(mut self, name: impl Into<String>) -> Self {
821 self.hints.push(ModelHint::new(name));
822 self
823 }
824}
825
826#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
828#[serde(rename_all = "camelCase")]
829pub enum IncludeContext {
830 AllServers,
832 ThisServer,
834 #[default]
836 None,
837}
838
839#[derive(Debug, Clone, Serialize, Deserialize)]
841pub struct SamplingMessage {
842 pub role: ContentRole,
844 pub content: SamplingContent,
846}
847
848impl SamplingMessage {
849 pub fn user(text: impl Into<String>) -> Self {
851 Self {
852 role: ContentRole::User,
853 content: SamplingContent::Text { text: text.into() },
854 }
855 }
856
857 pub fn assistant(text: impl Into<String>) -> Self {
859 Self {
860 role: ContentRole::Assistant,
861 content: SamplingContent::Text { text: text.into() },
862 }
863 }
864}
865
866#[derive(Debug, Clone, Serialize, Deserialize)]
870#[serde(rename_all = "camelCase")]
871pub struct SamplingTool {
872 pub name: String,
874 #[serde(skip_serializing_if = "Option::is_none")]
876 pub description: Option<String>,
877 pub input_schema: Value,
879}
880
881#[derive(Debug, Clone, Serialize, Deserialize)]
885#[serde(rename_all = "camelCase")]
886pub struct ToolChoice {
887 #[serde(rename = "type")]
892 pub mode: String,
893}
894
895impl ToolChoice {
896 pub fn auto() -> Self {
898 Self {
899 mode: "auto".to_string(),
900 }
901 }
902
903 pub fn required() -> Self {
905 Self {
906 mode: "required".to_string(),
907 }
908 }
909
910 pub fn none() -> Self {
912 Self {
913 mode: "none".to_string(),
914 }
915 }
916}
917
918#[derive(Debug, Clone, Serialize, Deserialize)]
920#[serde(tag = "type", rename_all = "lowercase")]
921pub enum SamplingContent {
922 Text {
924 text: String,
926 },
927 Image {
929 data: String,
931 #[serde(rename = "mimeType")]
933 mime_type: String,
934 },
935 Audio {
937 data: String,
939 #[serde(rename = "mimeType")]
941 mime_type: String,
942 },
943 #[serde(rename = "tool_use")]
945 ToolUse {
946 id: String,
948 name: String,
950 input: Value,
952 },
953 #[serde(rename = "tool_result")]
955 ToolResult {
956 tool_use_id: String,
958 content: Vec<SamplingContent>,
960 #[serde(default, skip_serializing_if = "Option::is_none")]
962 is_error: Option<bool>,
963 },
964}
965
966impl SamplingContent {
967 pub fn as_text(&self) -> Option<&str> {
986 match self {
987 SamplingContent::Text { text } => Some(text),
988 _ => None,
989 }
990 }
991}
992
993#[derive(Debug, Clone, Serialize, Deserialize)]
998#[serde(untagged)]
999pub enum SamplingContentOrArray {
1000 Single(SamplingContent),
1002 Array(Vec<SamplingContent>),
1004}
1005
1006impl SamplingContentOrArray {
1007 pub fn items(&self) -> Vec<&SamplingContent> {
1009 match self {
1010 Self::Single(c) => vec![c],
1011 Self::Array(arr) => arr.iter().collect(),
1012 }
1013 }
1014
1015 pub fn into_items(self) -> Vec<SamplingContent> {
1017 match self {
1018 Self::Single(c) => vec![c],
1019 Self::Array(arr) => arr,
1020 }
1021 }
1022}
1023
1024#[derive(Debug, Clone, Serialize, Deserialize)]
1026#[serde(rename_all = "camelCase")]
1027pub struct CreateMessageParams {
1028 pub messages: Vec<SamplingMessage>,
1030 pub max_tokens: u32,
1032 #[serde(default, skip_serializing_if = "Option::is_none")]
1034 pub system_prompt: Option<String>,
1035 #[serde(default, skip_serializing_if = "Option::is_none")]
1037 pub temperature: Option<f64>,
1038 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1040 pub stop_sequences: Vec<String>,
1041 #[serde(default, skip_serializing_if = "Option::is_none")]
1043 pub model_preferences: Option<ModelPreferences>,
1044 #[serde(default, skip_serializing_if = "Option::is_none")]
1046 pub include_context: Option<IncludeContext>,
1047 #[serde(default, skip_serializing_if = "Option::is_none")]
1049 pub metadata: Option<serde_json::Map<String, Value>>,
1050 #[serde(default, skip_serializing_if = "Option::is_none")]
1052 pub tools: Option<Vec<SamplingTool>>,
1053 #[serde(default, skip_serializing_if = "Option::is_none")]
1055 pub tool_choice: Option<ToolChoice>,
1056}
1057
1058impl CreateMessageParams {
1059 pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
1061 Self {
1062 messages,
1063 max_tokens,
1064 system_prompt: None,
1065 temperature: None,
1066 stop_sequences: Vec::new(),
1067 model_preferences: None,
1068 include_context: None,
1069 metadata: None,
1070 tools: None,
1071 tool_choice: None,
1072 }
1073 }
1074
1075 pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
1077 self.system_prompt = Some(prompt.into());
1078 self
1079 }
1080
1081 pub fn temperature(mut self, temp: f64) -> Self {
1083 self.temperature = Some(temp.clamp(0.0, 1.0));
1084 self
1085 }
1086
1087 pub fn stop_sequence(mut self, seq: impl Into<String>) -> Self {
1089 self.stop_sequences.push(seq.into());
1090 self
1091 }
1092
1093 pub fn model_preferences(mut self, prefs: ModelPreferences) -> Self {
1095 self.model_preferences = Some(prefs);
1096 self
1097 }
1098
1099 pub fn include_context(mut self, mode: IncludeContext) -> Self {
1101 self.include_context = Some(mode);
1102 self
1103 }
1104
1105 pub fn tools(mut self, tools: Vec<SamplingTool>) -> Self {
1107 self.tools = Some(tools);
1108 self
1109 }
1110
1111 pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
1113 self.tool_choice = Some(choice);
1114 self
1115 }
1116}
1117
1118#[derive(Debug, Clone, Serialize, Deserialize)]
1120#[serde(rename_all = "camelCase")]
1121pub struct CreateMessageResult {
1122 pub content: SamplingContentOrArray,
1124 pub model: String,
1126 pub role: ContentRole,
1128 #[serde(default, skip_serializing_if = "Option::is_none")]
1130 pub stop_reason: Option<String>,
1131}
1132
1133impl CreateMessageResult {
1134 pub fn content_items(&self) -> Vec<&SamplingContent> {
1136 self.content.items()
1137 }
1138
1139 pub fn first_text(&self) -> Option<&str> {
1160 self.content.items().iter().find_map(|c| c.as_text())
1161 }
1162}
1163
1164#[derive(Debug, Clone, Default, Deserialize, Serialize)]
1166#[serde(rename_all = "camelCase")]
1167pub struct Implementation {
1168 pub name: String,
1170 pub version: String,
1172 #[serde(skip_serializing_if = "Option::is_none")]
1174 pub title: Option<String>,
1175 #[serde(skip_serializing_if = "Option::is_none")]
1177 pub description: Option<String>,
1178 #[serde(skip_serializing_if = "Option::is_none")]
1180 pub icons: Option<Vec<ToolIcon>>,
1181 #[serde(skip_serializing_if = "Option::is_none")]
1183 pub website_url: Option<String>,
1184}
1185
1186#[derive(Debug, Clone, Serialize, Deserialize)]
1187#[serde(rename_all = "camelCase")]
1188pub struct InitializeResult {
1189 pub protocol_version: String,
1190 pub capabilities: ServerCapabilities,
1191 pub server_info: Implementation,
1192 #[serde(skip_serializing_if = "Option::is_none")]
1195 pub instructions: Option<String>,
1196}
1197
1198#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1199#[serde(rename_all = "camelCase")]
1200pub struct ServerCapabilities {
1201 #[serde(default, skip_serializing_if = "Option::is_none")]
1202 pub tools: Option<ToolsCapability>,
1203 #[serde(default, skip_serializing_if = "Option::is_none")]
1204 pub resources: Option<ResourcesCapability>,
1205 #[serde(default, skip_serializing_if = "Option::is_none")]
1206 pub prompts: Option<PromptsCapability>,
1207 #[serde(default, skip_serializing_if = "Option::is_none")]
1209 pub logging: Option<LoggingCapability>,
1210 #[serde(default, skip_serializing_if = "Option::is_none")]
1211 pub tasks: Option<TasksCapability>,
1212 #[serde(default, skip_serializing_if = "Option::is_none")]
1214 pub completions: Option<CompletionsCapability>,
1215}
1216
1217#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1219pub struct LoggingCapability {}
1220
1221#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1223#[serde(rename_all = "camelCase")]
1224pub struct TasksCapability {
1225 #[serde(default, skip_serializing_if = "Option::is_none")]
1227 pub default_poll_interval: Option<u64>,
1228}
1229
1230#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1231#[serde(rename_all = "camelCase")]
1232pub struct ToolsCapability {
1233 #[serde(default)]
1234 pub list_changed: bool,
1235}
1236
1237#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1238#[serde(rename_all = "camelCase")]
1239pub struct ResourcesCapability {
1240 #[serde(default)]
1241 pub subscribe: bool,
1242 #[serde(default)]
1243 pub list_changed: bool,
1244}
1245
1246#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1247#[serde(rename_all = "camelCase")]
1248pub struct PromptsCapability {
1249 #[serde(default)]
1250 pub list_changed: bool,
1251}
1252
1253#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1258pub struct ListToolsParams {
1259 #[serde(default)]
1260 pub cursor: Option<String>,
1261}
1262
1263#[derive(Debug, Clone, Serialize, Deserialize)]
1264#[serde(rename_all = "camelCase")]
1265pub struct ListToolsResult {
1266 pub tools: Vec<ToolDefinition>,
1267 #[serde(default, skip_serializing_if = "Option::is_none")]
1268 pub next_cursor: Option<String>,
1269}
1270
1271#[derive(Debug, Clone, Serialize, Deserialize)]
1273#[serde(rename_all = "camelCase")]
1274pub struct ToolDefinition {
1275 pub name: String,
1276 #[serde(skip_serializing_if = "Option::is_none")]
1278 pub title: Option<String>,
1279 #[serde(skip_serializing_if = "Option::is_none")]
1280 pub description: Option<String>,
1281 pub input_schema: Value,
1282 #[serde(skip_serializing_if = "Option::is_none")]
1284 pub output_schema: Option<Value>,
1285 #[serde(skip_serializing_if = "Option::is_none")]
1287 pub icons: Option<Vec<ToolIcon>>,
1288 #[serde(skip_serializing_if = "Option::is_none")]
1291 pub annotations: Option<ToolAnnotations>,
1292}
1293
1294#[derive(Debug, Clone, Serialize, Deserialize)]
1296#[serde(rename_all = "camelCase")]
1297pub struct ToolIcon {
1298 pub src: String,
1300 #[serde(skip_serializing_if = "Option::is_none")]
1302 pub mime_type: Option<String>,
1303 #[serde(skip_serializing_if = "Option::is_none")]
1305 pub sizes: Option<Vec<String>>,
1306}
1307
1308#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1311#[serde(rename_all = "camelCase")]
1312pub struct ToolAnnotations {
1313 #[serde(skip_serializing_if = "Option::is_none")]
1315 pub title: Option<String>,
1316 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1318 pub read_only_hint: bool,
1319 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1322 pub destructive_hint: bool,
1323 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1326 pub idempotent_hint: bool,
1327 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1329 pub open_world_hint: bool,
1330}
1331
1332fn default_true() -> bool {
1333 true
1334}
1335
1336fn is_true(v: &bool) -> bool {
1337 *v
1338}
1339
1340#[derive(Debug, Clone, Serialize, Deserialize)]
1341pub struct CallToolParams {
1342 pub name: String,
1343 #[serde(default)]
1344 pub arguments: Value,
1345 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1347 pub meta: Option<RequestMeta>,
1348}
1349
1350#[derive(Debug, Clone, Serialize, Deserialize)]
1371#[serde(rename_all = "camelCase")]
1372pub struct CallToolResult {
1373 pub content: Vec<Content>,
1375 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1377 pub is_error: bool,
1378 #[serde(default, skip_serializing_if = "Option::is_none")]
1380 pub structured_content: Option<Value>,
1381}
1382
1383impl CallToolResult {
1384 pub fn text(text: impl Into<String>) -> Self {
1388 Self {
1389 content: vec![Content::Text {
1390 text: text.into(),
1391 annotations: None,
1392 }],
1393 is_error: false,
1394 structured_content: None,
1395 }
1396 }
1397
1398 pub fn error(message: impl Into<String>) -> Self {
1403 Self {
1404 content: vec![Content::Text {
1405 text: message.into(),
1406 annotations: None,
1407 }],
1408 is_error: true,
1409 structured_content: None,
1410 }
1411 }
1412
1413 pub fn json(value: Value) -> Self {
1421 let text = serde_json::to_string_pretty(&value).unwrap_or_default();
1422 Self {
1423 content: vec![Content::Text {
1424 text,
1425 annotations: None,
1426 }],
1427 is_error: false,
1428 structured_content: Some(value),
1429 }
1430 }
1431
1432 pub fn from_serialize(
1464 value: &impl serde::Serialize,
1465 ) -> std::result::Result<Self, crate::error::Error> {
1466 let json_value = serde_json::to_value(value)
1467 .map_err(|e| crate::error::Error::tool(format!("Serialization failed: {}", e)))?;
1468 Ok(Self::json(json_value))
1469 }
1470
1471 pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
1473 Self {
1474 content: vec![Content::Image {
1475 data: data.into(),
1476 mime_type: mime_type.into(),
1477 annotations: None,
1478 }],
1479 is_error: false,
1480 structured_content: None,
1481 }
1482 }
1483
1484 pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
1486 Self {
1487 content: vec![Content::Audio {
1488 data: data.into(),
1489 mime_type: mime_type.into(),
1490 annotations: None,
1491 }],
1492 is_error: false,
1493 structured_content: None,
1494 }
1495 }
1496
1497 pub fn resource_link(uri: impl Into<String>) -> Self {
1499 Self {
1500 content: vec![Content::ResourceLink {
1501 uri: uri.into(),
1502 name: None,
1503 description: None,
1504 mime_type: None,
1505 annotations: None,
1506 }],
1507 is_error: false,
1508 structured_content: None,
1509 }
1510 }
1511
1512 pub fn resource_link_with_meta(
1514 uri: impl Into<String>,
1515 name: Option<String>,
1516 description: Option<String>,
1517 mime_type: Option<String>,
1518 ) -> Self {
1519 Self {
1520 content: vec![Content::ResourceLink {
1521 uri: uri.into(),
1522 name,
1523 description,
1524 mime_type,
1525 annotations: None,
1526 }],
1527 is_error: false,
1528 structured_content: None,
1529 }
1530 }
1531
1532 pub fn resource(resource: ResourceContent) -> Self {
1534 Self {
1535 content: vec![Content::Resource {
1536 resource,
1537 annotations: None,
1538 }],
1539 is_error: false,
1540 structured_content: None,
1541 }
1542 }
1543
1544 pub fn all_text(&self) -> String {
1558 self.content.iter().filter_map(|c| c.as_text()).collect()
1559 }
1560
1561 pub fn first_text(&self) -> Option<&str> {
1574 self.content.iter().find_map(|c| c.as_text())
1575 }
1576}
1577
1578#[derive(Debug, Clone, Serialize, Deserialize)]
1583#[serde(tag = "type", rename_all = "snake_case")]
1584pub enum Content {
1585 Text {
1587 text: String,
1589 #[serde(skip_serializing_if = "Option::is_none")]
1591 annotations: Option<ContentAnnotations>,
1592 },
1593 Image {
1595 data: String,
1597 #[serde(rename = "mimeType")]
1599 mime_type: String,
1600 #[serde(skip_serializing_if = "Option::is_none")]
1602 annotations: Option<ContentAnnotations>,
1603 },
1604 Audio {
1606 data: String,
1608 #[serde(rename = "mimeType")]
1610 mime_type: String,
1611 #[serde(skip_serializing_if = "Option::is_none")]
1613 annotations: Option<ContentAnnotations>,
1614 },
1615 Resource {
1617 resource: ResourceContent,
1619 #[serde(skip_serializing_if = "Option::is_none")]
1621 annotations: Option<ContentAnnotations>,
1622 },
1623 ResourceLink {
1625 uri: String,
1627 #[serde(skip_serializing_if = "Option::is_none")]
1629 name: Option<String>,
1630 #[serde(skip_serializing_if = "Option::is_none")]
1632 description: Option<String>,
1633 #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
1635 mime_type: Option<String>,
1636 #[serde(skip_serializing_if = "Option::is_none")]
1637 annotations: Option<ContentAnnotations>,
1638 },
1639}
1640
1641#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1643pub struct ContentAnnotations {
1644 #[serde(skip_serializing_if = "Option::is_none")]
1646 pub audience: Option<Vec<ContentRole>>,
1647 #[serde(skip_serializing_if = "Option::is_none")]
1649 pub priority: Option<f64>,
1650}
1651
1652impl Content {
1653 pub fn as_text(&self) -> Option<&str> {
1666 match self {
1667 Content::Text { text, .. } => Some(text),
1668 _ => None,
1669 }
1670 }
1671}
1672
1673#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1677#[serde(rename_all = "lowercase")]
1678pub enum ContentRole {
1679 User,
1681 Assistant,
1683}
1684
1685#[derive(Debug, Clone, Serialize, Deserialize)]
1689#[serde(rename_all = "camelCase")]
1690pub struct ResourceContent {
1691 pub uri: String,
1693 #[serde(skip_serializing_if = "Option::is_none")]
1695 pub mime_type: Option<String>,
1696 #[serde(skip_serializing_if = "Option::is_none")]
1698 pub text: Option<String>,
1699 #[serde(skip_serializing_if = "Option::is_none")]
1701 pub blob: Option<String>,
1702}
1703
1704#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1709pub struct ListResourcesParams {
1710 #[serde(default)]
1711 pub cursor: Option<String>,
1712}
1713
1714#[derive(Debug, Clone, Serialize, Deserialize)]
1715#[serde(rename_all = "camelCase")]
1716pub struct ListResourcesResult {
1717 pub resources: Vec<ResourceDefinition>,
1718 #[serde(default, skip_serializing_if = "Option::is_none")]
1719 pub next_cursor: Option<String>,
1720}
1721
1722#[derive(Debug, Clone, Serialize, Deserialize)]
1723#[serde(rename_all = "camelCase")]
1724pub struct ResourceDefinition {
1725 pub uri: String,
1726 pub name: String,
1727 #[serde(skip_serializing_if = "Option::is_none")]
1729 pub title: Option<String>,
1730 #[serde(skip_serializing_if = "Option::is_none")]
1731 pub description: Option<String>,
1732 #[serde(skip_serializing_if = "Option::is_none")]
1733 pub mime_type: Option<String>,
1734 #[serde(skip_serializing_if = "Option::is_none")]
1736 pub icons: Option<Vec<ToolIcon>>,
1737 #[serde(skip_serializing_if = "Option::is_none")]
1739 pub size: Option<u64>,
1740}
1741
1742#[derive(Debug, Clone, Serialize, Deserialize)]
1743pub struct ReadResourceParams {
1744 pub uri: String,
1745}
1746
1747#[derive(Debug, Clone, Serialize, Deserialize)]
1748pub struct ReadResourceResult {
1749 pub contents: Vec<ResourceContent>,
1750}
1751
1752impl ReadResourceResult {
1753 pub fn text(uri: impl Into<String>, content: impl Into<String>) -> Self {
1763 Self {
1764 contents: vec![ResourceContent {
1765 uri: uri.into(),
1766 mime_type: Some("text/plain".to_string()),
1767 text: Some(content.into()),
1768 blob: None,
1769 }],
1770 }
1771 }
1772
1773 pub fn text_with_mime(
1787 uri: impl Into<String>,
1788 content: impl Into<String>,
1789 mime_type: impl Into<String>,
1790 ) -> Self {
1791 Self {
1792 contents: vec![ResourceContent {
1793 uri: uri.into(),
1794 mime_type: Some(mime_type.into()),
1795 text: Some(content.into()),
1796 blob: None,
1797 }],
1798 }
1799 }
1800
1801 pub fn json<T: serde::Serialize>(uri: impl Into<String>, value: &T) -> Self {
1815 let json_string =
1816 serde_json::to_string_pretty(value).unwrap_or_else(|_| "null".to_string());
1817 Self {
1818 contents: vec![ResourceContent {
1819 uri: uri.into(),
1820 mime_type: Some("application/json".to_string()),
1821 text: Some(json_string),
1822 blob: None,
1823 }],
1824 }
1825 }
1826
1827 pub fn blob(uri: impl Into<String>, bytes: &[u8]) -> Self {
1838 use base64::Engine;
1839 let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
1840 Self {
1841 contents: vec![ResourceContent {
1842 uri: uri.into(),
1843 mime_type: Some("application/octet-stream".to_string()),
1844 text: None,
1845 blob: Some(encoded),
1846 }],
1847 }
1848 }
1849
1850 pub fn blob_with_mime(
1861 uri: impl Into<String>,
1862 bytes: &[u8],
1863 mime_type: impl Into<String>,
1864 ) -> Self {
1865 use base64::Engine;
1866 let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
1867 Self {
1868 contents: vec![ResourceContent {
1869 uri: uri.into(),
1870 mime_type: Some(mime_type.into()),
1871 text: None,
1872 blob: Some(encoded),
1873 }],
1874 }
1875 }
1876
1877 pub fn first_text(&self) -> Option<&str> {
1890 self.contents.first().and_then(|c| c.text.as_deref())
1891 }
1892
1893 pub fn first_uri(&self) -> Option<&str> {
1906 self.contents.first().map(|c| c.uri.as_str())
1907 }
1908}
1909
1910#[derive(Debug, Clone, Deserialize)]
1911pub struct SubscribeResourceParams {
1912 pub uri: String,
1913}
1914
1915#[derive(Debug, Clone, Deserialize)]
1916pub struct UnsubscribeResourceParams {
1917 pub uri: String,
1918}
1919
1920#[derive(Debug, Clone, Default, Deserialize)]
1922pub struct ListResourceTemplatesParams {
1923 #[serde(default)]
1925 pub cursor: Option<String>,
1926}
1927
1928#[derive(Debug, Clone, Serialize)]
1930#[serde(rename_all = "camelCase")]
1931pub struct ListResourceTemplatesResult {
1932 pub resource_templates: Vec<ResourceTemplateDefinition>,
1934 #[serde(skip_serializing_if = "Option::is_none")]
1936 pub next_cursor: Option<String>,
1937}
1938
1939#[derive(Debug, Clone, Serialize, Deserialize)]
1955#[serde(rename_all = "camelCase")]
1956pub struct ResourceTemplateDefinition {
1957 pub uri_template: String,
1959 pub name: String,
1961 #[serde(skip_serializing_if = "Option::is_none")]
1963 pub title: Option<String>,
1964 #[serde(skip_serializing_if = "Option::is_none")]
1966 pub description: Option<String>,
1967 #[serde(skip_serializing_if = "Option::is_none")]
1969 pub mime_type: Option<String>,
1970 #[serde(skip_serializing_if = "Option::is_none")]
1972 pub icons: Option<Vec<ToolIcon>>,
1973}
1974
1975#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1980pub struct ListPromptsParams {
1981 #[serde(default)]
1982 pub cursor: Option<String>,
1983}
1984
1985#[derive(Debug, Clone, Serialize, Deserialize)]
1986#[serde(rename_all = "camelCase")]
1987pub struct ListPromptsResult {
1988 pub prompts: Vec<PromptDefinition>,
1989 #[serde(default, skip_serializing_if = "Option::is_none")]
1990 pub next_cursor: Option<String>,
1991}
1992
1993#[derive(Debug, Clone, Serialize, Deserialize)]
1994pub struct PromptDefinition {
1995 pub name: String,
1996 #[serde(skip_serializing_if = "Option::is_none")]
1998 pub title: Option<String>,
1999 #[serde(skip_serializing_if = "Option::is_none")]
2000 pub description: Option<String>,
2001 #[serde(skip_serializing_if = "Option::is_none")]
2003 pub icons: Option<Vec<ToolIcon>>,
2004 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2005 pub arguments: Vec<PromptArgument>,
2006}
2007
2008#[derive(Debug, Clone, Serialize, Deserialize)]
2009pub struct PromptArgument {
2010 pub name: String,
2011 #[serde(skip_serializing_if = "Option::is_none")]
2012 pub description: Option<String>,
2013 #[serde(default)]
2014 pub required: bool,
2015}
2016
2017#[derive(Debug, Clone, Serialize, Deserialize)]
2018pub struct GetPromptParams {
2019 pub name: String,
2020 #[serde(default)]
2021 pub arguments: std::collections::HashMap<String, String>,
2022}
2023
2024#[derive(Debug, Clone, Serialize, Deserialize)]
2025pub struct GetPromptResult {
2026 #[serde(default, skip_serializing_if = "Option::is_none")]
2027 pub description: Option<String>,
2028 pub messages: Vec<PromptMessage>,
2029}
2030
2031impl GetPromptResult {
2032 pub fn user_message(text: impl Into<String>) -> Self {
2042 Self {
2043 description: None,
2044 messages: vec![PromptMessage {
2045 role: PromptRole::User,
2046 content: Content::Text {
2047 text: text.into(),
2048 annotations: None,
2049 },
2050 }],
2051 }
2052 }
2053
2054 pub fn user_message_with_description(
2067 text: impl Into<String>,
2068 description: impl Into<String>,
2069 ) -> Self {
2070 Self {
2071 description: Some(description.into()),
2072 messages: vec![PromptMessage {
2073 role: PromptRole::User,
2074 content: Content::Text {
2075 text: text.into(),
2076 annotations: None,
2077 },
2078 }],
2079 }
2080 }
2081
2082 pub fn assistant_message(text: impl Into<String>) -> Self {
2092 Self {
2093 description: None,
2094 messages: vec![PromptMessage {
2095 role: PromptRole::Assistant,
2096 content: Content::Text {
2097 text: text.into(),
2098 annotations: None,
2099 },
2100 }],
2101 }
2102 }
2103
2104 pub fn builder() -> GetPromptResultBuilder {
2119 GetPromptResultBuilder::new()
2120 }
2121
2122 pub fn first_message_text(&self) -> Option<&str> {
2136 self.messages.first().and_then(|m| m.content.as_text())
2137 }
2138}
2139
2140#[derive(Debug, Clone, Default)]
2142pub struct GetPromptResultBuilder {
2143 description: Option<String>,
2144 messages: Vec<PromptMessage>,
2145}
2146
2147impl GetPromptResultBuilder {
2148 pub fn new() -> Self {
2150 Self::default()
2151 }
2152
2153 pub fn description(mut self, description: impl Into<String>) -> Self {
2155 self.description = Some(description.into());
2156 self
2157 }
2158
2159 pub fn user(mut self, text: impl Into<String>) -> Self {
2161 self.messages.push(PromptMessage {
2162 role: PromptRole::User,
2163 content: Content::Text {
2164 text: text.into(),
2165 annotations: None,
2166 },
2167 });
2168 self
2169 }
2170
2171 pub fn assistant(mut self, text: impl Into<String>) -> Self {
2173 self.messages.push(PromptMessage {
2174 role: PromptRole::Assistant,
2175 content: Content::Text {
2176 text: text.into(),
2177 annotations: None,
2178 },
2179 });
2180 self
2181 }
2182
2183 pub fn build(self) -> GetPromptResult {
2185 GetPromptResult {
2186 description: self.description,
2187 messages: self.messages,
2188 }
2189 }
2190}
2191
2192#[derive(Debug, Clone, Serialize, Deserialize)]
2193pub struct PromptMessage {
2194 pub role: PromptRole,
2195 pub content: Content,
2196}
2197
2198#[derive(Debug, Clone, Serialize, Deserialize)]
2199#[serde(rename_all = "lowercase")]
2200pub enum PromptRole {
2201 User,
2202 Assistant,
2203}
2204
2205#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2211#[serde(rename_all = "snake_case")]
2212pub enum TaskStatus {
2213 Working,
2215 InputRequired,
2217 Completed,
2219 Failed,
2221 Cancelled,
2223}
2224
2225impl std::fmt::Display for TaskStatus {
2226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2227 match self {
2228 TaskStatus::Working => write!(f, "working"),
2229 TaskStatus::InputRequired => write!(f, "input_required"),
2230 TaskStatus::Completed => write!(f, "completed"),
2231 TaskStatus::Failed => write!(f, "failed"),
2232 TaskStatus::Cancelled => write!(f, "cancelled"),
2233 }
2234 }
2235}
2236
2237impl TaskStatus {
2238 pub fn is_terminal(&self) -> bool {
2240 matches!(
2241 self,
2242 TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
2243 )
2244 }
2245}
2246
2247#[derive(Debug, Clone, Serialize, Deserialize)]
2249#[serde(rename_all = "camelCase")]
2250pub struct TaskInfo {
2251 pub task_id: String,
2253 pub status: TaskStatus,
2255 pub created_at: String,
2257 #[serde(skip_serializing_if = "Option::is_none")]
2259 pub ttl: Option<u64>,
2260 #[serde(skip_serializing_if = "Option::is_none")]
2262 pub poll_interval: Option<u64>,
2263 #[serde(skip_serializing_if = "Option::is_none")]
2265 pub progress: Option<f64>,
2266 #[serde(skip_serializing_if = "Option::is_none")]
2268 pub message: Option<String>,
2269}
2270
2271#[derive(Debug, Clone, Deserialize)]
2273#[serde(rename_all = "camelCase")]
2274pub struct EnqueueTaskParams {
2275 pub tool_name: String,
2277 #[serde(default)]
2279 pub arguments: Value,
2280 #[serde(default)]
2282 pub ttl: Option<u64>,
2283}
2284
2285#[derive(Debug, Clone, Serialize)]
2287#[serde(rename_all = "camelCase")]
2288pub struct EnqueueTaskResult {
2289 pub task_id: String,
2291 pub status: TaskStatus,
2293 #[serde(skip_serializing_if = "Option::is_none")]
2295 pub poll_interval: Option<u64>,
2296}
2297
2298#[derive(Debug, Clone, Default, Deserialize)]
2300#[serde(rename_all = "camelCase")]
2301pub struct ListTasksParams {
2302 #[serde(default)]
2304 pub status: Option<TaskStatus>,
2305 #[serde(default)]
2307 pub cursor: Option<String>,
2308}
2309
2310#[derive(Debug, Clone, Serialize)]
2312#[serde(rename_all = "camelCase")]
2313pub struct ListTasksResult {
2314 pub tasks: Vec<TaskInfo>,
2316 #[serde(skip_serializing_if = "Option::is_none")]
2318 pub next_cursor: Option<String>,
2319}
2320
2321#[derive(Debug, Clone, Deserialize)]
2323#[serde(rename_all = "camelCase")]
2324pub struct GetTaskInfoParams {
2325 pub task_id: String,
2327}
2328
2329pub type GetTaskInfoResult = TaskInfo;
2331
2332#[derive(Debug, Clone, Deserialize)]
2334#[serde(rename_all = "camelCase")]
2335pub struct GetTaskResultParams {
2336 pub task_id: String,
2338}
2339
2340#[derive(Debug, Clone, Serialize)]
2342#[serde(rename_all = "camelCase")]
2343pub struct GetTaskResultResult {
2344 pub task_id: String,
2346 pub status: TaskStatus,
2348 #[serde(skip_serializing_if = "Option::is_none")]
2350 pub result: Option<CallToolResult>,
2351 #[serde(skip_serializing_if = "Option::is_none")]
2353 pub error: Option<String>,
2354}
2355
2356#[derive(Debug, Clone, Deserialize)]
2358#[serde(rename_all = "camelCase")]
2359pub struct CancelTaskParams {
2360 pub task_id: String,
2362 #[serde(default)]
2364 pub reason: Option<String>,
2365}
2366
2367#[derive(Debug, Clone, Serialize)]
2369#[serde(rename_all = "camelCase")]
2370pub struct CancelTaskResult {
2371 pub cancelled: bool,
2373 pub status: TaskStatus,
2375}
2376
2377#[derive(Debug, Clone, Serialize, Deserialize)]
2379#[serde(rename_all = "camelCase")]
2380pub struct TaskStatusChangedParams {
2381 pub task_id: String,
2383 pub status: TaskStatus,
2385 #[serde(skip_serializing_if = "Option::is_none")]
2387 pub message: Option<String>,
2388}
2389
2390#[derive(Debug, Clone, Serialize, Deserialize)]
2396#[serde(rename_all = "camelCase")]
2397pub struct ElicitFormParams {
2398 pub mode: ElicitMode,
2400 pub message: String,
2402 pub requested_schema: ElicitFormSchema,
2404 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2406 pub meta: Option<RequestMeta>,
2407}
2408
2409#[derive(Debug, Clone, Serialize, Deserialize)]
2411#[serde(rename_all = "camelCase")]
2412pub struct ElicitUrlParams {
2413 pub mode: ElicitMode,
2415 pub elicitation_id: String,
2417 pub message: String,
2419 pub url: String,
2421 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2423 pub meta: Option<RequestMeta>,
2424}
2425
2426#[derive(Debug, Clone, Serialize, Deserialize)]
2428#[serde(untagged)]
2429pub enum ElicitRequestParams {
2430 Form(ElicitFormParams),
2431 Url(ElicitUrlParams),
2432}
2433
2434#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2436#[serde(rename_all = "lowercase")]
2437pub enum ElicitMode {
2438 Form,
2440 Url,
2442}
2443
2444#[derive(Debug, Clone, Serialize, Deserialize)]
2448pub struct ElicitFormSchema {
2449 #[serde(rename = "type")]
2451 pub schema_type: String,
2452 pub properties: std::collections::HashMap<String, PrimitiveSchemaDefinition>,
2454 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2456 pub required: Vec<String>,
2457}
2458
2459impl ElicitFormSchema {
2460 pub fn new() -> Self {
2462 Self {
2463 schema_type: "object".to_string(),
2464 properties: std::collections::HashMap::new(),
2465 required: Vec::new(),
2466 }
2467 }
2468
2469 pub fn string_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
2471 self.properties.insert(
2472 name.to_string(),
2473 PrimitiveSchemaDefinition::String(StringSchema {
2474 schema_type: "string".to_string(),
2475 description: description.map(|s| s.to_string()),
2476 format: None,
2477 min_length: None,
2478 max_length: None,
2479 default: None,
2480 }),
2481 );
2482 if required {
2483 self.required.push(name.to_string());
2484 }
2485 self
2486 }
2487
2488 pub fn string_field_with_default(
2490 mut self,
2491 name: &str,
2492 description: Option<&str>,
2493 required: bool,
2494 default: &str,
2495 ) -> Self {
2496 self.properties.insert(
2497 name.to_string(),
2498 PrimitiveSchemaDefinition::String(StringSchema {
2499 schema_type: "string".to_string(),
2500 description: description.map(|s| s.to_string()),
2501 format: None,
2502 min_length: None,
2503 max_length: None,
2504 default: Some(default.to_string()),
2505 }),
2506 );
2507 if required {
2508 self.required.push(name.to_string());
2509 }
2510 self
2511 }
2512
2513 pub fn integer_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
2515 self.properties.insert(
2516 name.to_string(),
2517 PrimitiveSchemaDefinition::Integer(IntegerSchema {
2518 schema_type: "integer".to_string(),
2519 description: description.map(|s| s.to_string()),
2520 minimum: None,
2521 maximum: None,
2522 default: None,
2523 }),
2524 );
2525 if required {
2526 self.required.push(name.to_string());
2527 }
2528 self
2529 }
2530
2531 pub fn integer_field_with_default(
2533 mut self,
2534 name: &str,
2535 description: Option<&str>,
2536 required: bool,
2537 default: i64,
2538 ) -> Self {
2539 self.properties.insert(
2540 name.to_string(),
2541 PrimitiveSchemaDefinition::Integer(IntegerSchema {
2542 schema_type: "integer".to_string(),
2543 description: description.map(|s| s.to_string()),
2544 minimum: None,
2545 maximum: None,
2546 default: Some(default),
2547 }),
2548 );
2549 if required {
2550 self.required.push(name.to_string());
2551 }
2552 self
2553 }
2554
2555 pub fn number_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
2557 self.properties.insert(
2558 name.to_string(),
2559 PrimitiveSchemaDefinition::Number(NumberSchema {
2560 schema_type: "number".to_string(),
2561 description: description.map(|s| s.to_string()),
2562 minimum: None,
2563 maximum: None,
2564 default: None,
2565 }),
2566 );
2567 if required {
2568 self.required.push(name.to_string());
2569 }
2570 self
2571 }
2572
2573 pub fn number_field_with_default(
2575 mut self,
2576 name: &str,
2577 description: Option<&str>,
2578 required: bool,
2579 default: f64,
2580 ) -> Self {
2581 self.properties.insert(
2582 name.to_string(),
2583 PrimitiveSchemaDefinition::Number(NumberSchema {
2584 schema_type: "number".to_string(),
2585 description: description.map(|s| s.to_string()),
2586 minimum: None,
2587 maximum: None,
2588 default: Some(default),
2589 }),
2590 );
2591 if required {
2592 self.required.push(name.to_string());
2593 }
2594 self
2595 }
2596
2597 pub fn boolean_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
2599 self.properties.insert(
2600 name.to_string(),
2601 PrimitiveSchemaDefinition::Boolean(BooleanSchema {
2602 schema_type: "boolean".to_string(),
2603 description: description.map(|s| s.to_string()),
2604 default: None,
2605 }),
2606 );
2607 if required {
2608 self.required.push(name.to_string());
2609 }
2610 self
2611 }
2612
2613 pub fn boolean_field_with_default(
2615 mut self,
2616 name: &str,
2617 description: Option<&str>,
2618 required: bool,
2619 default: bool,
2620 ) -> Self {
2621 self.properties.insert(
2622 name.to_string(),
2623 PrimitiveSchemaDefinition::Boolean(BooleanSchema {
2624 schema_type: "boolean".to_string(),
2625 description: description.map(|s| s.to_string()),
2626 default: Some(default),
2627 }),
2628 );
2629 if required {
2630 self.required.push(name.to_string());
2631 }
2632 self
2633 }
2634
2635 pub fn enum_field(
2637 mut self,
2638 name: &str,
2639 description: Option<&str>,
2640 options: Vec<String>,
2641 required: bool,
2642 ) -> Self {
2643 self.properties.insert(
2644 name.to_string(),
2645 PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
2646 schema_type: "string".to_string(),
2647 description: description.map(|s| s.to_string()),
2648 enum_values: options,
2649 default: None,
2650 }),
2651 );
2652 if required {
2653 self.required.push(name.to_string());
2654 }
2655 self
2656 }
2657
2658 pub fn enum_field_with_default(
2660 mut self,
2661 name: &str,
2662 description: Option<&str>,
2663 required: bool,
2664 options: &[&str],
2665 default: &str,
2666 ) -> Self {
2667 self.properties.insert(
2668 name.to_string(),
2669 PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
2670 schema_type: "string".to_string(),
2671 description: description.map(|s| s.to_string()),
2672 enum_values: options.iter().map(|s| s.to_string()).collect(),
2673 default: Some(default.to_string()),
2674 }),
2675 );
2676 if required {
2677 self.required.push(name.to_string());
2678 }
2679 self
2680 }
2681
2682 pub fn raw_field(mut self, name: &str, schema: serde_json::Value, required: bool) -> Self {
2686 self.properties
2687 .insert(name.to_string(), PrimitiveSchemaDefinition::Raw(schema));
2688 if required {
2689 self.required.push(name.to_string());
2690 }
2691 self
2692 }
2693}
2694
2695impl Default for ElicitFormSchema {
2696 fn default() -> Self {
2697 Self::new()
2698 }
2699}
2700
2701#[derive(Debug, Clone, Serialize, Deserialize)]
2703#[serde(untagged)]
2704pub enum PrimitiveSchemaDefinition {
2705 String(StringSchema),
2707 Integer(IntegerSchema),
2709 Number(NumberSchema),
2711 Boolean(BooleanSchema),
2713 SingleSelectEnum(SingleSelectEnumSchema),
2715 MultiSelectEnum(MultiSelectEnumSchema),
2717 Raw(serde_json::Value),
2719}
2720
2721#[derive(Debug, Clone, Serialize, Deserialize)]
2723#[serde(rename_all = "camelCase")]
2724pub struct StringSchema {
2725 #[serde(rename = "type")]
2726 pub schema_type: String,
2727 #[serde(skip_serializing_if = "Option::is_none")]
2728 pub description: Option<String>,
2729 #[serde(skip_serializing_if = "Option::is_none")]
2730 pub format: Option<String>,
2731 #[serde(skip_serializing_if = "Option::is_none")]
2732 pub min_length: Option<u64>,
2733 #[serde(skip_serializing_if = "Option::is_none")]
2734 pub max_length: Option<u64>,
2735 #[serde(skip_serializing_if = "Option::is_none")]
2737 pub default: Option<String>,
2738}
2739
2740#[derive(Debug, Clone, Serialize, Deserialize)]
2742#[serde(rename_all = "camelCase")]
2743pub struct IntegerSchema {
2744 #[serde(rename = "type")]
2745 pub schema_type: String,
2746 #[serde(skip_serializing_if = "Option::is_none")]
2747 pub description: Option<String>,
2748 #[serde(skip_serializing_if = "Option::is_none")]
2749 pub minimum: Option<i64>,
2750 #[serde(skip_serializing_if = "Option::is_none")]
2751 pub maximum: Option<i64>,
2752 #[serde(skip_serializing_if = "Option::is_none")]
2754 pub default: Option<i64>,
2755}
2756
2757#[derive(Debug, Clone, Serialize, Deserialize)]
2759#[serde(rename_all = "camelCase")]
2760pub struct NumberSchema {
2761 #[serde(rename = "type")]
2762 pub schema_type: String,
2763 #[serde(skip_serializing_if = "Option::is_none")]
2764 pub description: Option<String>,
2765 #[serde(skip_serializing_if = "Option::is_none")]
2766 pub minimum: Option<f64>,
2767 #[serde(skip_serializing_if = "Option::is_none")]
2768 pub maximum: Option<f64>,
2769 #[serde(skip_serializing_if = "Option::is_none")]
2771 pub default: Option<f64>,
2772}
2773
2774#[derive(Debug, Clone, Serialize, Deserialize)]
2776#[serde(rename_all = "camelCase")]
2777pub struct BooleanSchema {
2778 #[serde(rename = "type")]
2779 pub schema_type: String,
2780 #[serde(skip_serializing_if = "Option::is_none")]
2781 pub description: Option<String>,
2782 #[serde(skip_serializing_if = "Option::is_none")]
2784 pub default: Option<bool>,
2785}
2786
2787#[derive(Debug, Clone, Serialize, Deserialize)]
2789#[serde(rename_all = "camelCase")]
2790pub struct SingleSelectEnumSchema {
2791 #[serde(rename = "type")]
2792 pub schema_type: String,
2793 #[serde(skip_serializing_if = "Option::is_none")]
2794 pub description: Option<String>,
2795 #[serde(rename = "enum")]
2796 pub enum_values: Vec<String>,
2797 #[serde(skip_serializing_if = "Option::is_none")]
2799 pub default: Option<String>,
2800}
2801
2802#[derive(Debug, Clone, Serialize, Deserialize)]
2804#[serde(rename_all = "camelCase")]
2805pub struct MultiSelectEnumSchema {
2806 #[serde(rename = "type")]
2807 pub schema_type: String,
2808 #[serde(skip_serializing_if = "Option::is_none")]
2809 pub description: Option<String>,
2810 pub items: MultiSelectEnumItems,
2811 #[serde(skip_serializing_if = "Option::is_none")]
2812 pub unique_items: Option<bool>,
2813}
2814
2815#[derive(Debug, Clone, Serialize, Deserialize)]
2817pub struct MultiSelectEnumItems {
2818 #[serde(rename = "type")]
2819 pub schema_type: String,
2820 #[serde(rename = "enum")]
2821 pub enum_values: Vec<String>,
2822}
2823
2824#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2826#[serde(rename_all = "lowercase")]
2827pub enum ElicitAction {
2828 Accept,
2830 Decline,
2832 Cancel,
2834}
2835
2836#[derive(Debug, Clone, Serialize, Deserialize)]
2838pub struct ElicitResult {
2839 pub action: ElicitAction,
2841 #[serde(default, skip_serializing_if = "Option::is_none")]
2843 pub content: Option<std::collections::HashMap<String, ElicitFieldValue>>,
2844}
2845
2846impl ElicitResult {
2847 pub fn accept(content: std::collections::HashMap<String, ElicitFieldValue>) -> Self {
2849 Self {
2850 action: ElicitAction::Accept,
2851 content: Some(content),
2852 }
2853 }
2854
2855 pub fn decline() -> Self {
2857 Self {
2858 action: ElicitAction::Decline,
2859 content: None,
2860 }
2861 }
2862
2863 pub fn cancel() -> Self {
2865 Self {
2866 action: ElicitAction::Cancel,
2867 content: None,
2868 }
2869 }
2870}
2871
2872#[derive(Debug, Clone, Serialize, Deserialize)]
2874#[serde(untagged)]
2875pub enum ElicitFieldValue {
2876 String(String),
2877 Number(f64),
2878 Integer(i64),
2879 Boolean(bool),
2880 StringArray(Vec<String>),
2881}
2882
2883#[derive(Debug, Clone, Serialize, Deserialize)]
2885#[serde(rename_all = "camelCase")]
2886pub struct ElicitationCompleteParams {
2887 pub elicitation_id: String,
2889}
2890
2891#[derive(Debug, Clone, Default, Serialize)]
2896pub struct EmptyResult {}
2897
2898impl McpRequest {
2903 pub fn from_jsonrpc(req: &JsonRpcRequest) -> Result<Self, crate::error::Error> {
2905 let params = req
2906 .params
2907 .clone()
2908 .unwrap_or(Value::Object(Default::default()));
2909
2910 match req.method.as_str() {
2911 "initialize" => {
2912 let p: InitializeParams = serde_json::from_value(params)?;
2913 Ok(McpRequest::Initialize(p))
2914 }
2915 "tools/list" => {
2916 let p: ListToolsParams = serde_json::from_value(params).unwrap_or_default();
2917 Ok(McpRequest::ListTools(p))
2918 }
2919 "tools/call" => {
2920 let p: CallToolParams = serde_json::from_value(params)?;
2921 Ok(McpRequest::CallTool(p))
2922 }
2923 "resources/list" => {
2924 let p: ListResourcesParams = serde_json::from_value(params).unwrap_or_default();
2925 Ok(McpRequest::ListResources(p))
2926 }
2927 "resources/templates/list" => {
2928 let p: ListResourceTemplatesParams =
2929 serde_json::from_value(params).unwrap_or_default();
2930 Ok(McpRequest::ListResourceTemplates(p))
2931 }
2932 "resources/read" => {
2933 let p: ReadResourceParams = serde_json::from_value(params)?;
2934 Ok(McpRequest::ReadResource(p))
2935 }
2936 "resources/subscribe" => {
2937 let p: SubscribeResourceParams = serde_json::from_value(params)?;
2938 Ok(McpRequest::SubscribeResource(p))
2939 }
2940 "resources/unsubscribe" => {
2941 let p: UnsubscribeResourceParams = serde_json::from_value(params)?;
2942 Ok(McpRequest::UnsubscribeResource(p))
2943 }
2944 "prompts/list" => {
2945 let p: ListPromptsParams = serde_json::from_value(params).unwrap_or_default();
2946 Ok(McpRequest::ListPrompts(p))
2947 }
2948 "prompts/get" => {
2949 let p: GetPromptParams = serde_json::from_value(params)?;
2950 Ok(McpRequest::GetPrompt(p))
2951 }
2952 "tasks/enqueue" => {
2953 let p: EnqueueTaskParams = serde_json::from_value(params)?;
2954 Ok(McpRequest::EnqueueTask(p))
2955 }
2956 "tasks/list" => {
2957 let p: ListTasksParams = serde_json::from_value(params).unwrap_or_default();
2958 Ok(McpRequest::ListTasks(p))
2959 }
2960 "tasks/get" => {
2961 let p: GetTaskInfoParams = serde_json::from_value(params)?;
2962 Ok(McpRequest::GetTaskInfo(p))
2963 }
2964 "tasks/result" => {
2965 let p: GetTaskResultParams = serde_json::from_value(params)?;
2966 Ok(McpRequest::GetTaskResult(p))
2967 }
2968 "tasks/cancel" => {
2969 let p: CancelTaskParams = serde_json::from_value(params)?;
2970 Ok(McpRequest::CancelTask(p))
2971 }
2972 "ping" => Ok(McpRequest::Ping),
2973 "logging/setLevel" => {
2974 let p: SetLogLevelParams = serde_json::from_value(params)?;
2975 Ok(McpRequest::SetLoggingLevel(p))
2976 }
2977 "completion/complete" => {
2978 let p: CompleteParams = serde_json::from_value(params)?;
2979 Ok(McpRequest::Complete(p))
2980 }
2981 method => Ok(McpRequest::Unknown {
2982 method: method.to_string(),
2983 params: req.params.clone(),
2984 }),
2985 }
2986 }
2987}
2988
2989impl McpNotification {
2990 pub fn from_jsonrpc(notif: &JsonRpcNotification) -> Result<Self, crate::error::Error> {
2992 let params = notif
2993 .params
2994 .clone()
2995 .unwrap_or(Value::Object(Default::default()));
2996
2997 match notif.method.as_str() {
2998 notifications::INITIALIZED => Ok(McpNotification::Initialized),
2999 notifications::CANCELLED => {
3000 let p: CancelledParams = serde_json::from_value(params)?;
3001 Ok(McpNotification::Cancelled(p))
3002 }
3003 notifications::PROGRESS => {
3004 let p: ProgressParams = serde_json::from_value(params)?;
3005 Ok(McpNotification::Progress(p))
3006 }
3007 notifications::ROOTS_LIST_CHANGED => Ok(McpNotification::RootsListChanged),
3008 method => Ok(McpNotification::Unknown {
3009 method: method.to_string(),
3010 params: notif.params.clone(),
3011 }),
3012 }
3013 }
3014}
3015
3016#[cfg(test)]
3017mod tests {
3018 use super::*;
3019
3020 #[test]
3021 fn test_elicit_form_schema_builder() {
3022 let schema = ElicitFormSchema::new()
3023 .string_field("name", Some("Your name"), true)
3024 .number_field("age", Some("Your age"), false)
3025 .boolean_field("agree", Some("Do you agree?"), true)
3026 .enum_field(
3027 "color",
3028 Some("Favorite color"),
3029 vec!["red".to_string(), "green".to_string(), "blue".to_string()],
3030 false,
3031 );
3032
3033 assert_eq!(schema.schema_type, "object");
3034 assert_eq!(schema.properties.len(), 4);
3035 assert_eq!(schema.required.len(), 2);
3036 assert!(schema.required.contains(&"name".to_string()));
3037 assert!(schema.required.contains(&"agree".to_string()));
3038 }
3039
3040 #[test]
3041 fn test_elicit_form_schema_serialization() {
3042 let schema = ElicitFormSchema::new().string_field("username", Some("Enter username"), true);
3043
3044 let json = serde_json::to_value(&schema).unwrap();
3045 assert_eq!(json["type"], "object");
3046 assert!(json["properties"]["username"]["type"] == "string");
3047 assert!(
3048 json["required"]
3049 .as_array()
3050 .unwrap()
3051 .contains(&serde_json::json!("username"))
3052 );
3053 }
3054
3055 #[test]
3056 fn test_elicit_result_accept() {
3057 let mut content = std::collections::HashMap::new();
3058 content.insert(
3059 "name".to_string(),
3060 ElicitFieldValue::String("Alice".to_string()),
3061 );
3062 content.insert("age".to_string(), ElicitFieldValue::Integer(30));
3063
3064 let result = ElicitResult::accept(content);
3065 assert_eq!(result.action, ElicitAction::Accept);
3066 assert!(result.content.is_some());
3067 }
3068
3069 #[test]
3070 fn test_elicit_result_decline() {
3071 let result = ElicitResult::decline();
3072 assert_eq!(result.action, ElicitAction::Decline);
3073 assert!(result.content.is_none());
3074 }
3075
3076 #[test]
3077 fn test_elicit_result_cancel() {
3078 let result = ElicitResult::cancel();
3079 assert_eq!(result.action, ElicitAction::Cancel);
3080 assert!(result.content.is_none());
3081 }
3082
3083 #[test]
3084 fn test_elicit_mode_serialization() {
3085 assert_eq!(
3086 serde_json::to_string(&ElicitMode::Form).unwrap(),
3087 "\"form\""
3088 );
3089 assert_eq!(serde_json::to_string(&ElicitMode::Url).unwrap(), "\"url\"");
3090 }
3091
3092 #[test]
3093 fn test_elicit_action_serialization() {
3094 assert_eq!(
3095 serde_json::to_string(&ElicitAction::Accept).unwrap(),
3096 "\"accept\""
3097 );
3098 assert_eq!(
3099 serde_json::to_string(&ElicitAction::Decline).unwrap(),
3100 "\"decline\""
3101 );
3102 assert_eq!(
3103 serde_json::to_string(&ElicitAction::Cancel).unwrap(),
3104 "\"cancel\""
3105 );
3106 }
3107
3108 #[test]
3109 fn test_elicitation_capability() {
3110 let cap = ElicitationCapability {
3111 form: Some(ElicitationFormCapability {}),
3112 url: None,
3113 };
3114
3115 let json = serde_json::to_value(&cap).unwrap();
3116 assert!(json["form"].is_object());
3117 assert!(json.get("url").is_none());
3118 }
3119
3120 #[test]
3121 fn test_client_capabilities_with_elicitation() {
3122 let caps = ClientCapabilities {
3123 roots: None,
3124 sampling: None,
3125 elicitation: Some(ElicitationCapability {
3126 form: Some(ElicitationFormCapability {}),
3127 url: Some(ElicitationUrlCapability {}),
3128 }),
3129 };
3130
3131 let json = serde_json::to_value(&caps).unwrap();
3132 assert!(json["elicitation"]["form"].is_object());
3133 assert!(json["elicitation"]["url"].is_object());
3134 }
3135
3136 #[test]
3137 fn test_elicit_url_params() {
3138 let params = ElicitUrlParams {
3139 mode: ElicitMode::Url,
3140 elicitation_id: "abc123".to_string(),
3141 message: "Please authorize".to_string(),
3142 url: "https://example.com/auth".to_string(),
3143 meta: None,
3144 };
3145
3146 let json = serde_json::to_value(¶ms).unwrap();
3147 assert_eq!(json["mode"], "url");
3148 assert_eq!(json["elicitationId"], "abc123");
3149 assert_eq!(json["message"], "Please authorize");
3150 assert_eq!(json["url"], "https://example.com/auth");
3151 }
3152
3153 #[test]
3154 fn test_elicitation_complete_params() {
3155 let params = ElicitationCompleteParams {
3156 elicitation_id: "xyz789".to_string(),
3157 };
3158
3159 let json = serde_json::to_value(¶ms).unwrap();
3160 assert_eq!(json["elicitationId"], "xyz789");
3161 }
3162
3163 #[test]
3164 fn test_root_new() {
3165 let root = Root::new("file:///home/user/project");
3166 assert_eq!(root.uri, "file:///home/user/project");
3167 assert!(root.name.is_none());
3168 }
3169
3170 #[test]
3171 fn test_root_with_name() {
3172 let root = Root::with_name("file:///home/user/project", "My Project");
3173 assert_eq!(root.uri, "file:///home/user/project");
3174 assert_eq!(root.name.as_deref(), Some("My Project"));
3175 }
3176
3177 #[test]
3178 fn test_root_serialization() {
3179 let root = Root::with_name("file:///workspace", "Workspace");
3180 let json = serde_json::to_value(&root).unwrap();
3181 assert_eq!(json["uri"], "file:///workspace");
3182 assert_eq!(json["name"], "Workspace");
3183 }
3184
3185 #[test]
3186 fn test_root_serialization_without_name() {
3187 let root = Root::new("file:///workspace");
3188 let json = serde_json::to_value(&root).unwrap();
3189 assert_eq!(json["uri"], "file:///workspace");
3190 assert!(json.get("name").is_none());
3191 }
3192
3193 #[test]
3194 fn test_root_deserialization() {
3195 let json = serde_json::json!({
3196 "uri": "file:///home/user",
3197 "name": "Home"
3198 });
3199 let root: Root = serde_json::from_value(json).unwrap();
3200 assert_eq!(root.uri, "file:///home/user");
3201 assert_eq!(root.name.as_deref(), Some("Home"));
3202 }
3203
3204 #[test]
3205 fn test_list_roots_result() {
3206 let result = ListRootsResult {
3207 roots: vec![
3208 Root::new("file:///project1"),
3209 Root::with_name("file:///project2", "Project 2"),
3210 ],
3211 };
3212
3213 let json = serde_json::to_value(&result).unwrap();
3214 let roots = json["roots"].as_array().unwrap();
3215 assert_eq!(roots.len(), 2);
3216 assert_eq!(roots[0]["uri"], "file:///project1");
3217 assert_eq!(roots[1]["name"], "Project 2");
3218 }
3219
3220 #[test]
3221 fn test_roots_capability_serialization() {
3222 let cap = RootsCapability { list_changed: true };
3223 let json = serde_json::to_value(&cap).unwrap();
3224 assert_eq!(json["listChanged"], true);
3225 }
3226
3227 #[test]
3228 fn test_client_capabilities_with_roots() {
3229 let caps = ClientCapabilities {
3230 roots: Some(RootsCapability { list_changed: true }),
3231 sampling: None,
3232 elicitation: None,
3233 };
3234
3235 let json = serde_json::to_value(&caps).unwrap();
3236 assert_eq!(json["roots"]["listChanged"], true);
3237 }
3238
3239 #[test]
3240 fn test_roots_list_changed_notification_parsing() {
3241 let notif = JsonRpcNotification {
3242 jsonrpc: "2.0".to_string(),
3243 method: notifications::ROOTS_LIST_CHANGED.to_string(),
3244 params: None,
3245 };
3246
3247 let mcp_notif = McpNotification::from_jsonrpc(¬if).unwrap();
3248 assert!(matches!(mcp_notif, McpNotification::RootsListChanged));
3249 }
3250
3251 #[test]
3256 fn test_prompt_reference() {
3257 let ref_ = PromptReference::new("my-prompt");
3258 assert_eq!(ref_.ref_type, "ref/prompt");
3259 assert_eq!(ref_.name, "my-prompt");
3260
3261 let json = serde_json::to_value(&ref_).unwrap();
3262 assert_eq!(json["type"], "ref/prompt");
3263 assert_eq!(json["name"], "my-prompt");
3264 }
3265
3266 #[test]
3267 fn test_resource_reference() {
3268 let ref_ = ResourceReference::new("file:///path/to/file");
3269 assert_eq!(ref_.ref_type, "ref/resource");
3270 assert_eq!(ref_.uri, "file:///path/to/file");
3271
3272 let json = serde_json::to_value(&ref_).unwrap();
3273 assert_eq!(json["type"], "ref/resource");
3274 assert_eq!(json["uri"], "file:///path/to/file");
3275 }
3276
3277 #[test]
3278 fn test_completion_reference_prompt() {
3279 let ref_ = CompletionReference::prompt("test-prompt");
3280 let json = serde_json::to_value(&ref_).unwrap();
3281 assert_eq!(json["type"], "ref/prompt");
3282 assert_eq!(json["name"], "test-prompt");
3283 }
3284
3285 #[test]
3286 fn test_completion_reference_resource() {
3287 let ref_ = CompletionReference::resource("file:///test");
3288 let json = serde_json::to_value(&ref_).unwrap();
3289 assert_eq!(json["type"], "ref/resource");
3290 assert_eq!(json["uri"], "file:///test");
3291 }
3292
3293 #[test]
3294 fn test_completion_argument() {
3295 let arg = CompletionArgument::new("query", "SELECT * FROM");
3296 assert_eq!(arg.name, "query");
3297 assert_eq!(arg.value, "SELECT * FROM");
3298 }
3299
3300 #[test]
3301 fn test_complete_params_serialization() {
3302 let params = CompleteParams {
3303 reference: CompletionReference::prompt("sql-prompt"),
3304 argument: CompletionArgument::new("query", "SEL"),
3305 };
3306
3307 let json = serde_json::to_value(¶ms).unwrap();
3308 assert_eq!(json["ref"]["type"], "ref/prompt");
3309 assert_eq!(json["ref"]["name"], "sql-prompt");
3310 assert_eq!(json["argument"]["name"], "query");
3311 assert_eq!(json["argument"]["value"], "SEL");
3312 }
3313
3314 #[test]
3315 fn test_completion_new() {
3316 let completion = Completion::new(vec!["SELECT".to_string(), "SET".to_string()]);
3317 assert_eq!(completion.values.len(), 2);
3318 assert!(completion.total.is_none());
3319 assert!(completion.has_more.is_none());
3320 }
3321
3322 #[test]
3323 fn test_completion_with_pagination() {
3324 let completion =
3325 Completion::with_pagination(vec!["a".to_string(), "b".to_string()], 100, true);
3326 assert_eq!(completion.values.len(), 2);
3327 assert_eq!(completion.total, Some(100));
3328 assert_eq!(completion.has_more, Some(true));
3329 }
3330
3331 #[test]
3332 fn test_complete_result() {
3333 let result = CompleteResult::new(vec!["option1".to_string(), "option2".to_string()]);
3334 let json = serde_json::to_value(&result).unwrap();
3335 assert!(json["completion"]["values"].is_array());
3336 assert_eq!(json["completion"]["values"][0], "option1");
3337 }
3338
3339 #[test]
3344 fn test_model_hint() {
3345 let hint = ModelHint::new("claude-3-opus");
3346 assert_eq!(hint.name, "claude-3-opus");
3347 }
3348
3349 #[test]
3350 fn test_model_preferences_builder() {
3351 let prefs = ModelPreferences::new()
3352 .speed(0.8)
3353 .intelligence(0.9)
3354 .cost(0.5)
3355 .hint("gpt-4")
3356 .hint("claude-3");
3357
3358 assert_eq!(prefs.speed_priority, Some(0.8));
3359 assert_eq!(prefs.intelligence_priority, Some(0.9));
3360 assert_eq!(prefs.cost_priority, Some(0.5));
3361 assert_eq!(prefs.hints.len(), 2);
3362 }
3363
3364 #[test]
3365 fn test_model_preferences_clamping() {
3366 let prefs = ModelPreferences::new().speed(1.5).cost(-0.5);
3367
3368 assert_eq!(prefs.speed_priority, Some(1.0)); assert_eq!(prefs.cost_priority, Some(0.0)); }
3371
3372 #[test]
3373 fn test_include_context_serialization() {
3374 assert_eq!(
3375 serde_json::to_string(&IncludeContext::AllServers).unwrap(),
3376 "\"allServers\""
3377 );
3378 assert_eq!(
3379 serde_json::to_string(&IncludeContext::ThisServer).unwrap(),
3380 "\"thisServer\""
3381 );
3382 assert_eq!(
3383 serde_json::to_string(&IncludeContext::None).unwrap(),
3384 "\"none\""
3385 );
3386 }
3387
3388 #[test]
3389 fn test_sampling_message_user() {
3390 let msg = SamplingMessage::user("Hello, how are you?");
3391 assert_eq!(msg.role, ContentRole::User);
3392 assert!(
3393 matches!(msg.content, SamplingContent::Text { text } if text == "Hello, how are you?")
3394 );
3395 }
3396
3397 #[test]
3398 fn test_sampling_message_assistant() {
3399 let msg = SamplingMessage::assistant("I'm doing well!");
3400 assert_eq!(msg.role, ContentRole::Assistant);
3401 }
3402
3403 #[test]
3404 fn test_sampling_content_text_serialization() {
3405 let content = SamplingContent::Text {
3406 text: "Hello".to_string(),
3407 };
3408 let json = serde_json::to_value(&content).unwrap();
3409 assert_eq!(json["type"], "text");
3410 assert_eq!(json["text"], "Hello");
3411 }
3412
3413 #[test]
3414 fn test_sampling_content_image_serialization() {
3415 let content = SamplingContent::Image {
3416 data: "base64data".to_string(),
3417 mime_type: "image/png".to_string(),
3418 };
3419 let json = serde_json::to_value(&content).unwrap();
3420 assert_eq!(json["type"], "image");
3421 assert_eq!(json["data"], "base64data");
3422 assert_eq!(json["mimeType"], "image/png");
3423 }
3424
3425 #[test]
3426 fn test_create_message_params() {
3427 let params = CreateMessageParams::new(
3428 vec![
3429 SamplingMessage::user("What is 2+2?"),
3430 SamplingMessage::assistant("4"),
3431 SamplingMessage::user("And 3+3?"),
3432 ],
3433 100,
3434 )
3435 .system_prompt("You are a math tutor")
3436 .temperature(0.7)
3437 .stop_sequence("END")
3438 .include_context(IncludeContext::ThisServer);
3439
3440 assert_eq!(params.messages.len(), 3);
3441 assert_eq!(params.max_tokens, 100);
3442 assert_eq!(
3443 params.system_prompt.as_deref(),
3444 Some("You are a math tutor")
3445 );
3446 assert_eq!(params.temperature, Some(0.7));
3447 assert_eq!(params.stop_sequences.len(), 1);
3448 assert_eq!(params.include_context, Some(IncludeContext::ThisServer));
3449 }
3450
3451 #[test]
3452 fn test_create_message_params_serialization() {
3453 let params = CreateMessageParams::new(vec![SamplingMessage::user("Hello")], 50);
3454
3455 let json = serde_json::to_value(¶ms).unwrap();
3456 assert!(json["messages"].is_array());
3457 assert_eq!(json["maxTokens"], 50);
3458 }
3459
3460 #[test]
3461 fn test_create_message_result_deserialization() {
3462 let json = serde_json::json!({
3463 "content": {
3464 "type": "text",
3465 "text": "The answer is 42"
3466 },
3467 "model": "claude-3-opus",
3468 "role": "assistant",
3469 "stopReason": "end_turn"
3470 });
3471
3472 let result: CreateMessageResult = serde_json::from_value(json).unwrap();
3473 assert_eq!(result.model, "claude-3-opus");
3474 assert_eq!(result.role, ContentRole::Assistant);
3475 assert_eq!(result.stop_reason.as_deref(), Some("end_turn"));
3476 }
3477
3478 #[test]
3479 fn test_completions_capability_serialization() {
3480 let cap = CompletionsCapability {};
3481 let json = serde_json::to_value(&cap).unwrap();
3482 assert!(json.is_object());
3483 }
3484
3485 #[test]
3486 fn test_server_capabilities_with_completions() {
3487 let caps = ServerCapabilities {
3488 completions: Some(CompletionsCapability {}),
3489 ..Default::default()
3490 };
3491
3492 let json = serde_json::to_value(&caps).unwrap();
3493 assert!(json["completions"].is_object());
3494 }
3495
3496 #[test]
3497 fn test_content_resource_link_serialization() {
3498 let content = Content::ResourceLink {
3499 uri: "file:///test.txt".to_string(),
3500 name: Some("test.txt".to_string()),
3501 description: Some("A test file".to_string()),
3502 mime_type: Some("text/plain".to_string()),
3503 annotations: None,
3504 };
3505 let json = serde_json::to_value(&content).unwrap();
3506 assert_eq!(json["type"], "resource_link");
3507 assert_eq!(json["uri"], "file:///test.txt");
3508 assert_eq!(json["name"], "test.txt");
3509 assert_eq!(json["description"], "A test file");
3510 assert_eq!(json["mimeType"], "text/plain");
3511 }
3512
3513 #[test]
3514 fn test_call_tool_result_resource_link() {
3515 let result = CallToolResult::resource_link("file:///output.json");
3516 assert_eq!(result.content.len(), 1);
3517 assert!(!result.is_error);
3518 match &result.content[0] {
3519 Content::ResourceLink { uri, .. } => assert_eq!(uri, "file:///output.json"),
3520 _ => panic!("Expected ResourceLink content"),
3521 }
3522 }
3523
3524 #[test]
3525 fn test_call_tool_result_image() {
3526 let result = CallToolResult::image("base64data", "image/png");
3527 assert_eq!(result.content.len(), 1);
3528 match &result.content[0] {
3529 Content::Image {
3530 data, mime_type, ..
3531 } => {
3532 assert_eq!(data, "base64data");
3533 assert_eq!(mime_type, "image/png");
3534 }
3535 _ => panic!("Expected Image content"),
3536 }
3537 }
3538
3539 #[test]
3540 fn test_call_tool_result_audio() {
3541 let result = CallToolResult::audio("audiodata", "audio/wav");
3542 assert_eq!(result.content.len(), 1);
3543 match &result.content[0] {
3544 Content::Audio {
3545 data, mime_type, ..
3546 } => {
3547 assert_eq!(data, "audiodata");
3548 assert_eq!(mime_type, "audio/wav");
3549 }
3550 _ => panic!("Expected Audio content"),
3551 }
3552 }
3553
3554 #[test]
3555 fn test_sampling_tool_serialization() {
3556 let tool = SamplingTool {
3557 name: "get_weather".to_string(),
3558 description: Some("Get current weather".to_string()),
3559 input_schema: serde_json::json!({
3560 "type": "object",
3561 "properties": {
3562 "location": { "type": "string" }
3563 }
3564 }),
3565 };
3566 let json = serde_json::to_value(&tool).unwrap();
3567 assert_eq!(json["name"], "get_weather");
3568 assert_eq!(json["description"], "Get current weather");
3569 assert!(json["inputSchema"]["properties"]["location"].is_object());
3570 }
3571
3572 #[test]
3573 fn test_tool_choice_modes() {
3574 let auto = ToolChoice::auto();
3575 assert_eq!(auto.mode, "auto");
3576
3577 let required = ToolChoice::required();
3578 assert_eq!(required.mode, "required");
3579
3580 let none = ToolChoice::none();
3581 assert_eq!(none.mode, "none");
3582
3583 let json = serde_json::to_value(&auto).unwrap();
3585 assert_eq!(json["type"], "auto");
3586 }
3587
3588 #[test]
3589 fn test_sampling_content_tool_use() {
3590 let content = SamplingContent::ToolUse {
3591 id: "tool_123".to_string(),
3592 name: "get_weather".to_string(),
3593 input: serde_json::json!({"location": "San Francisco"}),
3594 };
3595 let json = serde_json::to_value(&content).unwrap();
3596 assert_eq!(json["type"], "tool_use");
3597 assert_eq!(json["id"], "tool_123");
3598 assert_eq!(json["name"], "get_weather");
3599 assert_eq!(json["input"]["location"], "San Francisco");
3600 }
3601
3602 #[test]
3603 fn test_sampling_content_tool_result() {
3604 let content = SamplingContent::ToolResult {
3605 tool_use_id: "tool_123".to_string(),
3606 content: vec![SamplingContent::Text {
3607 text: "72F, sunny".to_string(),
3608 }],
3609 is_error: None,
3610 };
3611 let json = serde_json::to_value(&content).unwrap();
3612 assert_eq!(json["type"], "tool_result");
3613 assert_eq!(json["tool_use_id"], "tool_123");
3614 assert_eq!(json["content"][0]["type"], "text");
3615 }
3616
3617 #[test]
3618 fn test_sampling_content_or_array_single() {
3619 let json = serde_json::json!({
3620 "type": "text",
3621 "text": "Hello"
3622 });
3623 let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
3624 let items = content.items();
3625 assert_eq!(items.len(), 1);
3626 match items[0] {
3627 SamplingContent::Text { text } => assert_eq!(text, "Hello"),
3628 _ => panic!("Expected text content"),
3629 }
3630 }
3631
3632 #[test]
3633 fn test_sampling_content_or_array_multiple() {
3634 let json = serde_json::json!([
3635 { "type": "text", "text": "Hello" },
3636 { "type": "text", "text": "World" }
3637 ]);
3638 let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
3639 let items = content.items();
3640 assert_eq!(items.len(), 2);
3641 }
3642
3643 #[test]
3644 fn test_create_message_params_with_tools() {
3645 let tool = SamplingTool {
3646 name: "calculator".to_string(),
3647 description: Some("Do math".to_string()),
3648 input_schema: serde_json::json!({"type": "object"}),
3649 };
3650 let params = CreateMessageParams::new(vec![], 100)
3651 .tools(vec![tool])
3652 .tool_choice(ToolChoice::auto());
3653
3654 let json = serde_json::to_value(¶ms).unwrap();
3655 assert!(json["tools"].is_array());
3656 assert_eq!(json["tools"][0]["name"], "calculator");
3657 assert_eq!(json["toolChoice"]["type"], "auto");
3658 }
3659
3660 #[test]
3661 fn test_create_message_result_content_items() {
3662 let result = CreateMessageResult {
3663 content: SamplingContentOrArray::Array(vec![
3664 SamplingContent::Text {
3665 text: "First".to_string(),
3666 },
3667 SamplingContent::Text {
3668 text: "Second".to_string(),
3669 },
3670 ]),
3671 model: "test".to_string(),
3672 role: ContentRole::Assistant,
3673 stop_reason: None,
3674 };
3675 let items = result.content_items();
3676 assert_eq!(items.len(), 2);
3677 }
3678
3679 #[test]
3680 fn test_sampling_content_as_text() {
3681 let text_content = SamplingContent::Text {
3682 text: "Hello".to_string(),
3683 };
3684 assert_eq!(text_content.as_text(), Some("Hello"));
3685
3686 let image_content = SamplingContent::Image {
3687 data: "base64data".to_string(),
3688 mime_type: "image/png".to_string(),
3689 };
3690 assert_eq!(image_content.as_text(), None);
3691
3692 let audio_content = SamplingContent::Audio {
3693 data: "base64audio".to_string(),
3694 mime_type: "audio/wav".to_string(),
3695 };
3696 assert_eq!(audio_content.as_text(), None);
3697 }
3698
3699 #[test]
3700 fn test_create_message_result_first_text_single() {
3701 let result = CreateMessageResult {
3702 content: SamplingContentOrArray::Single(SamplingContent::Text {
3703 text: "Hello, world!".to_string(),
3704 }),
3705 model: "test".to_string(),
3706 role: ContentRole::Assistant,
3707 stop_reason: None,
3708 };
3709 assert_eq!(result.first_text(), Some("Hello, world!"));
3710 }
3711
3712 #[test]
3713 fn test_create_message_result_first_text_array() {
3714 let result = CreateMessageResult {
3715 content: SamplingContentOrArray::Array(vec![
3716 SamplingContent::Text {
3717 text: "First".to_string(),
3718 },
3719 SamplingContent::Text {
3720 text: "Second".to_string(),
3721 },
3722 ]),
3723 model: "test".to_string(),
3724 role: ContentRole::Assistant,
3725 stop_reason: None,
3726 };
3727 assert_eq!(result.first_text(), Some("First"));
3728 }
3729
3730 #[test]
3731 fn test_create_message_result_first_text_skips_non_text() {
3732 let result = CreateMessageResult {
3733 content: SamplingContentOrArray::Array(vec![
3734 SamplingContent::Image {
3735 data: "base64data".to_string(),
3736 mime_type: "image/png".to_string(),
3737 },
3738 SamplingContent::Text {
3739 text: "After image".to_string(),
3740 },
3741 ]),
3742 model: "test".to_string(),
3743 role: ContentRole::Assistant,
3744 stop_reason: None,
3745 };
3746 assert_eq!(result.first_text(), Some("After image"));
3747 }
3748
3749 #[test]
3750 fn test_create_message_result_first_text_none() {
3751 let result = CreateMessageResult {
3752 content: SamplingContentOrArray::Single(SamplingContent::Image {
3753 data: "base64data".to_string(),
3754 mime_type: "image/png".to_string(),
3755 }),
3756 model: "test".to_string(),
3757 role: ContentRole::Assistant,
3758 stop_reason: None,
3759 };
3760 assert_eq!(result.first_text(), None);
3761 }
3762}