1use std::collections::HashMap;
7
8use serde::de::DeserializeOwned;
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12use crate::error::JsonRpcError;
13
14pub const JSONRPC_VERSION: &str = "2.0";
16
17pub const LATEST_PROTOCOL_VERSION: &str = "2025-11-25";
19
20pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &["2025-11-25", "2025-03-26"];
33
34pub const UPCOMING_PROTOCOL_VERSION: &str = "2026-07-28";
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct JsonRpcRequest {
50 pub jsonrpc: String,
52 pub id: RequestId,
54 pub method: String,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub params: Option<Value>,
59}
60
61impl JsonRpcRequest {
62 pub fn new(id: impl Into<RequestId>, method: impl Into<String>) -> Self {
64 Self {
65 jsonrpc: JSONRPC_VERSION.to_string(),
66 id: id.into(),
67 method: method.into(),
68 params: None,
69 }
70 }
71
72 pub fn with_params(mut self, params: Value) -> Self {
74 self.params = Some(params);
75 self
76 }
77
78 pub fn validate(&self) -> Result<(), JsonRpcError> {
81 if self.jsonrpc != JSONRPC_VERSION {
82 return Err(JsonRpcError::invalid_request(format!(
83 "Invalid JSON-RPC version: expected '{}', got '{}'",
84 JSONRPC_VERSION, self.jsonrpc
85 )));
86 }
87 Ok(())
88 }
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct JsonRpcResultResponse {
94 pub jsonrpc: String,
96 pub id: RequestId,
98 pub result: Value,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct JsonRpcErrorResponse {
105 pub jsonrpc: String,
107 #[serde(default)]
110 pub id: Option<RequestId>,
111 pub error: JsonRpcError,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(untagged)]
118#[non_exhaustive]
119pub enum JsonRpcResponse {
120 Result(JsonRpcResultResponse),
122 Error(JsonRpcErrorResponse),
124}
125
126impl JsonRpcResponse {
127 pub fn result(id: RequestId, result: Value) -> Self {
129 Self::Result(JsonRpcResultResponse {
130 jsonrpc: JSONRPC_VERSION.to_string(),
131 id,
132 result,
133 })
134 }
135
136 pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
138 Self::Error(JsonRpcErrorResponse {
139 jsonrpc: JSONRPC_VERSION.to_string(),
140 id,
141 error,
142 })
143 }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148#[serde(untagged)]
149#[non_exhaustive]
150pub enum JsonRpcMessage {
151 Single(JsonRpcRequest),
153 Batch(Vec<JsonRpcRequest>),
155}
156
157impl JsonRpcMessage {
158 pub fn is_batch(&self) -> bool {
160 matches!(self, JsonRpcMessage::Batch(_))
161 }
162
163 pub fn len(&self) -> usize {
165 match self {
166 JsonRpcMessage::Single(_) => 1,
167 JsonRpcMessage::Batch(batch) => batch.len(),
168 }
169 }
170
171 pub fn is_empty(&self) -> bool {
173 self.len() == 0
174 }
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(untagged)]
180#[non_exhaustive]
181pub enum JsonRpcResponseMessage {
182 Single(JsonRpcResponse),
184 Batch(Vec<JsonRpcResponse>),
186}
187
188impl JsonRpcResponseMessage {
189 pub fn is_batch(&self) -> bool {
191 matches!(self, JsonRpcResponseMessage::Batch(_))
192 }
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct JsonRpcNotification {
198 pub jsonrpc: String,
199 pub method: String,
200 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub params: Option<Value>,
202}
203
204impl JsonRpcNotification {
205 pub fn new(method: impl Into<String>) -> Self {
206 Self {
207 jsonrpc: JSONRPC_VERSION.to_string(),
208 method: method.into(),
209 params: None,
210 }
211 }
212
213 pub fn with_params(mut self, params: Value) -> Self {
214 self.params = Some(params);
215 self
216 }
217}
218
219pub mod notifications {
221 pub const INITIALIZED: &str = "notifications/initialized";
223 pub const CANCELLED: &str = "notifications/cancelled";
225 pub const PROGRESS: &str = "notifications/progress";
227 pub const TOOLS_LIST_CHANGED: &str = "notifications/tools/list_changed";
229 pub const RESOURCES_LIST_CHANGED: &str = "notifications/resources/list_changed";
231 pub const RESOURCE_UPDATED: &str = "notifications/resources/updated";
233 pub const PROMPTS_LIST_CHANGED: &str = "notifications/prompts/list_changed";
235 pub const ROOTS_LIST_CHANGED: &str = "notifications/roots/list_changed";
237 pub const MESSAGE: &str = "notifications/message";
239 pub const TASK_STATUS_CHANGED: &str = "notifications/tasks/status";
241 pub const ELICITATION_COMPLETE: &str = "notifications/elicitation/complete";
243}
244
245#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
249#[serde(rename_all = "lowercase")]
250#[non_exhaustive]
251pub enum LogLevel {
252 Emergency,
254 Alert,
256 Critical,
258 Error,
260 Warning,
262 Notice,
264 #[default]
266 Info,
267 Debug,
269}
270
271impl std::fmt::Display for LogLevel {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 match self {
274 LogLevel::Emergency => write!(f, "emergency"),
275 LogLevel::Alert => write!(f, "alert"),
276 LogLevel::Critical => write!(f, "critical"),
277 LogLevel::Error => write!(f, "error"),
278 LogLevel::Warning => write!(f, "warning"),
279 LogLevel::Notice => write!(f, "notice"),
280 LogLevel::Info => write!(f, "info"),
281 LogLevel::Debug => write!(f, "debug"),
282 }
283 }
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct LoggingMessageParams {
289 pub level: LogLevel,
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub logger: Option<String>,
294 #[serde(default)]
296 pub data: Value,
297 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
299 pub meta: Option<Value>,
300}
301
302impl LoggingMessageParams {
303 pub fn new(level: LogLevel, data: impl Into<Value>) -> Self {
305 Self {
306 level,
307 logger: None,
308 data: data.into(),
309 meta: None,
310 }
311 }
312
313 pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
315 self.logger = Some(logger.into());
316 self
317 }
318
319 pub fn with_data(mut self, data: impl Into<Value>) -> Self {
321 self.data = data.into();
322 self
323 }
324}
325
326#[derive(Debug, Clone, Deserialize)]
328pub struct SetLogLevelParams {
329 pub level: LogLevel,
331 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
333 pub meta: Option<RequestMeta>,
334}
335
336#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
338#[serde(untagged)]
339#[non_exhaustive]
340pub enum RequestId {
341 String(String),
342 Number(i64),
343}
344
345impl From<String> for RequestId {
346 fn from(s: String) -> Self {
347 RequestId::String(s)
348 }
349}
350
351impl From<&str> for RequestId {
352 fn from(s: &str) -> Self {
353 RequestId::String(s.to_string())
354 }
355}
356
357impl From<i64> for RequestId {
358 fn from(n: i64) -> Self {
359 RequestId::Number(n)
360 }
361}
362
363impl From<i32> for RequestId {
364 fn from(n: i32) -> Self {
365 RequestId::Number(n as i64)
366 }
367}
368
369#[allow(clippy::large_enum_variant)]
376#[derive(Debug, Clone)]
377#[non_exhaustive]
378pub enum McpRequest {
379 Initialize(InitializeParams),
381 ListTools(ListToolsParams),
383 CallTool(CallToolParams),
385 ListResources(ListResourcesParams),
387 ListResourceTemplates(ListResourceTemplatesParams),
389 ReadResource(ReadResourceParams),
391 SubscribeResource(SubscribeResourceParams),
393 UnsubscribeResource(UnsubscribeResourceParams),
395 ListPrompts(ListPromptsParams),
397 GetPrompt(GetPromptParams),
399 ListTasks(ListTasksParams),
405 GetTaskInfo(GetTaskInfoParams),
407 UpdateTask(UpdateTaskParams),
413 GetTaskResult(GetTaskResultParams),
419 CancelTask(CancelTaskParams),
421 Ping,
423 SetLoggingLevel(SetLogLevelParams),
425 Complete(CompleteParams),
427 Discover(DiscoverParams),
429 MessagesListen(MessagesListenParams),
436 Unknown {
438 method: String,
439 params: Option<Value>,
440 },
441}
442
443impl McpRequest {
444 pub fn method_name(&self) -> &str {
446 match self {
447 McpRequest::Initialize(_) => "initialize",
448 McpRequest::ListTools(_) => "tools/list",
449 McpRequest::CallTool(_) => "tools/call",
450 McpRequest::ListResources(_) => "resources/list",
451 McpRequest::ListResourceTemplates(_) => "resources/templates/list",
452 McpRequest::ReadResource(_) => "resources/read",
453 McpRequest::SubscribeResource(_) => "resources/subscribe",
454 McpRequest::UnsubscribeResource(_) => "resources/unsubscribe",
455 McpRequest::ListPrompts(_) => "prompts/list",
456 McpRequest::GetPrompt(_) => "prompts/get",
457 McpRequest::ListTasks(_) => "tasks/list",
458 McpRequest::GetTaskInfo(_) => "tasks/get",
459 McpRequest::UpdateTask(_) => "tasks/update",
460 McpRequest::GetTaskResult(_) => "tasks/result",
461 McpRequest::CancelTask(_) => "tasks/cancel",
462 McpRequest::Ping => "ping",
463 McpRequest::SetLoggingLevel(_) => "logging/setLevel",
464 McpRequest::Complete(_) => "completion/complete",
465 McpRequest::Discover(_) => "server/discover",
466 McpRequest::MessagesListen(_) => "messages/listen",
467 McpRequest::Unknown { method, .. } => method,
468 }
469 }
470}
471
472#[derive(Debug, Clone)]
474#[non_exhaustive]
475pub enum McpNotification {
476 Initialized,
478 Cancelled(CancelledParams),
480 Progress(ProgressParams),
482 RootsListChanged,
484 Unknown {
486 method: String,
487 params: Option<Value>,
488 },
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize)]
493#[serde(rename_all = "camelCase")]
494pub struct CancelledParams {
495 #[serde(default)]
505 pub request_id: Option<RequestId>,
506 #[serde(skip_serializing_if = "Option::is_none")]
508 pub reason: Option<String>,
509 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
511 pub meta: Option<Value>,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
516#[serde(rename_all = "camelCase")]
517pub struct ProgressParams {
518 pub progress_token: ProgressToken,
520 pub progress: f64,
522 #[serde(skip_serializing_if = "Option::is_none")]
524 pub total: Option<f64>,
525 #[serde(skip_serializing_if = "Option::is_none")]
527 pub message: Option<String>,
528 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
530 pub meta: Option<Value>,
531}
532
533#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
535#[serde(untagged)]
536#[non_exhaustive]
537pub enum ProgressToken {
538 String(String),
539 Number(i64),
540}
541
542#[derive(Debug, Clone, Default, Serialize, Deserialize)]
544#[serde(rename_all = "camelCase")]
545pub struct RequestMeta {
546 #[serde(skip_serializing_if = "Option::is_none")]
548 pub progress_token: Option<ProgressToken>,
549}
550
551#[allow(clippy::large_enum_variant)]
554#[derive(Debug, Clone, Serialize, Deserialize)]
555#[serde(untagged)]
556#[non_exhaustive]
557pub enum McpResponse {
558 Initialize(InitializeResult),
559 ListTools(ListToolsResult),
560 CallTool(CallToolResult),
561 ListResources(ListResourcesResult),
562 ListResourceTemplates(ListResourceTemplatesResult),
563 ReadResource(ReadResourceResult),
564 SubscribeResource(EmptyResult),
565 UnsubscribeResource(EmptyResult),
566 ListPrompts(ListPromptsResult),
567 GetPrompt(GetPromptResult),
568 CreateTask(CreateTaskResult),
569 ListTasks(ListTasksResult),
570 GetTaskInfo(TaskObject),
571 UpdateTask(EmptyResult),
573 GetTaskResult(CallToolResult),
574 CancelTask(TaskObject),
580 SetLoggingLevel(EmptyResult),
581 Complete(CompleteResult),
582 Pong(EmptyResult),
583 Discover(DiscoverResult),
585 MessagesListen(MessagesListenResult),
592 Empty(EmptyResult),
593 Raw(Value),
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
602#[serde(rename_all = "camelCase")]
603pub struct InitializeParams {
604 pub protocol_version: String,
605 pub capabilities: ClientCapabilities,
606 pub client_info: Implementation,
607 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
609 pub meta: Option<Value>,
610}
611
612#[derive(Debug, Clone, Default, Serialize, Deserialize)]
613pub struct ClientCapabilities {
614 #[serde(default, skip_serializing_if = "Option::is_none")]
615 pub roots: Option<RootsCapability>,
616 #[serde(default, skip_serializing_if = "Option::is_none")]
617 pub sampling: Option<SamplingCapability>,
618 #[serde(default, skip_serializing_if = "Option::is_none")]
619 pub elicitation: Option<ElicitationCapability>,
620 #[serde(default, skip_serializing_if = "Option::is_none")]
621 pub tasks: Option<ClientTasksCapability>,
622 #[serde(default, skip_serializing_if = "Option::is_none")]
624 pub experimental: Option<HashMap<String, serde_json::Value>>,
625 #[serde(default, skip_serializing_if = "Option::is_none")]
627 pub extensions: Option<HashMap<String, serde_json::Value>>,
628}
629
630#[derive(Debug, Clone, Default, Serialize, Deserialize)]
632pub struct ElicitationCapability {
633 #[serde(default, skip_serializing_if = "Option::is_none")]
635 pub form: Option<ElicitationFormCapability>,
636 #[serde(default, skip_serializing_if = "Option::is_none")]
638 pub url: Option<ElicitationUrlCapability>,
639}
640
641#[derive(Debug, Clone, Default, Serialize, Deserialize)]
643pub struct ElicitationFormCapability {}
644
645#[derive(Debug, Clone, Default, Serialize, Deserialize)]
647pub struct ElicitationUrlCapability {}
648
649#[derive(Debug, Clone, Default, Serialize, Deserialize)]
651#[serde(rename_all = "camelCase")]
652pub struct ClientTasksCapability {
653 #[serde(default, skip_serializing_if = "Option::is_none")]
655 pub list: Option<ClientTasksListCapability>,
656 #[serde(default, skip_serializing_if = "Option::is_none")]
658 pub cancel: Option<ClientTasksCancelCapability>,
659 #[serde(default, skip_serializing_if = "Option::is_none")]
661 pub requests: Option<ClientTasksRequestsCapability>,
662}
663
664#[derive(Debug, Clone, Default, Serialize, Deserialize)]
666pub struct ClientTasksListCapability {}
667
668#[derive(Debug, Clone, Default, Serialize, Deserialize)]
670pub struct ClientTasksCancelCapability {}
671
672#[derive(Debug, Clone, Default, Serialize, Deserialize)]
674#[serde(rename_all = "camelCase")]
675pub struct ClientTasksRequestsCapability {
676 #[serde(default, skip_serializing_if = "Option::is_none")]
678 pub sampling: Option<ClientTasksSamplingCapability>,
679 #[serde(default, skip_serializing_if = "Option::is_none")]
681 pub elicitation: Option<ClientTasksElicitationCapability>,
682}
683
684#[derive(Debug, Clone, Default, Serialize, Deserialize)]
686#[serde(rename_all = "camelCase")]
687pub struct ClientTasksSamplingCapability {
688 #[serde(default, skip_serializing_if = "Option::is_none")]
690 pub create_message: Option<ClientTasksSamplingCreateMessageCapability>,
691}
692
693#[derive(Debug, Clone, Default, Serialize, Deserialize)]
695pub struct ClientTasksSamplingCreateMessageCapability {}
696
697#[derive(Debug, Clone, Default, Serialize, Deserialize)]
699#[serde(rename_all = "camelCase")]
700pub struct ClientTasksElicitationCapability {
701 #[serde(default, skip_serializing_if = "Option::is_none")]
703 pub create: Option<ClientTasksElicitationCreateCapability>,
704}
705
706#[derive(Debug, Clone, Default, Serialize, Deserialize)]
708pub struct ClientTasksElicitationCreateCapability {}
709
710#[derive(Debug, Clone, Default, Serialize, Deserialize)]
734#[serde(rename_all = "camelCase")]
735pub struct RootsCapability {
736 #[serde(default)]
738 pub list_changed: bool,
739 #[serde(default, skip_serializing_if = "Option::is_none")]
743 pub deprecated: Option<DeprecationInfo>,
744}
745
746#[derive(Debug, Clone, Serialize, Deserialize)]
766pub struct Root {
767 pub uri: String,
769 #[serde(default, skip_serializing_if = "Option::is_none")]
771 pub name: Option<String>,
772 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
774 pub meta: Option<Value>,
775}
776
777impl Root {
778 pub fn new(uri: impl Into<String>) -> Self {
780 Self {
781 uri: uri.into(),
782 name: None,
783 meta: None,
784 }
785 }
786
787 pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
789 Self {
790 uri: uri.into(),
791 name: Some(name.into()),
792 meta: None,
793 }
794 }
795}
796
797#[derive(Debug, Clone, Default, Serialize, Deserialize)]
804pub struct ListRootsParams {
805 #[serde(default, rename = "_meta", skip_serializing_if = "Option::is_none")]
807 pub meta: Option<RequestMeta>,
808}
809
810#[derive(Debug, Clone, Serialize, Deserialize)]
812pub struct ListRootsResult {
813 pub roots: Vec<Root>,
815 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
817 pub meta: Option<Value>,
818}
819
820#[derive(Debug, Clone, Default, Serialize, Deserialize)]
821pub struct SamplingCapability {
822 #[serde(default, skip_serializing_if = "Option::is_none")]
824 pub tools: Option<SamplingToolsCapability>,
825 #[serde(default, skip_serializing_if = "Option::is_none")]
827 pub context: Option<SamplingContextCapability>,
828 #[serde(default, skip_serializing_if = "Option::is_none")]
831 pub deprecated: Option<DeprecationInfo>,
832}
833
834#[derive(Debug, Clone, Default, Serialize, Deserialize)]
836pub struct SamplingToolsCapability {}
837
838#[derive(Debug, Clone, Default, Serialize, Deserialize)]
840pub struct SamplingContextCapability {}
841
842#[derive(Debug, Clone, Default, Serialize, Deserialize)]
844pub struct CompletionsCapability {}
845
846#[derive(Debug, Clone, Serialize, Deserialize)]
852pub struct PromptReference {
853 #[serde(rename = "type")]
855 pub ref_type: String,
856 pub name: String,
858}
859
860impl PromptReference {
861 pub fn new(name: impl Into<String>) -> Self {
863 Self {
864 ref_type: "ref/prompt".to_string(),
865 name: name.into(),
866 }
867 }
868}
869
870#[derive(Debug, Clone, Serialize, Deserialize)]
872pub struct ResourceReference {
873 #[serde(rename = "type")]
875 pub ref_type: String,
876 pub uri: String,
878}
879
880impl ResourceReference {
881 pub fn new(uri: impl Into<String>) -> Self {
883 Self {
884 ref_type: "ref/resource".to_string(),
885 uri: uri.into(),
886 }
887 }
888}
889
890#[derive(Debug, Clone, Serialize, Deserialize)]
892#[serde(tag = "type")]
893#[non_exhaustive]
894pub enum CompletionReference {
895 #[serde(rename = "ref/prompt")]
897 Prompt {
898 name: String,
900 },
901 #[serde(rename = "ref/resource")]
903 Resource {
904 uri: String,
906 },
907}
908
909impl CompletionReference {
910 pub fn prompt(name: impl Into<String>) -> Self {
912 Self::Prompt { name: name.into() }
913 }
914
915 pub fn resource(uri: impl Into<String>) -> Self {
917 Self::Resource { uri: uri.into() }
918 }
919}
920
921#[derive(Debug, Clone, Serialize, Deserialize)]
923pub struct CompletionArgument {
924 pub name: String,
926 pub value: String,
928}
929
930impl CompletionArgument {
931 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
933 Self {
934 name: name.into(),
935 value: value.into(),
936 }
937 }
938}
939
940#[derive(Debug, Clone, Serialize, Deserialize)]
942#[serde(rename_all = "camelCase")]
943pub struct CompleteParams {
944 #[serde(rename = "ref")]
946 pub reference: CompletionReference,
947 pub argument: CompletionArgument,
949 #[serde(default, skip_serializing_if = "Option::is_none")]
951 pub context: Option<CompletionContext>,
952 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
954 pub meta: Option<Value>,
955}
956
957#[derive(Debug, Clone, Serialize, Deserialize)]
959#[serde(rename_all = "camelCase")]
960pub struct CompletionContext {
961 #[serde(default, skip_serializing_if = "Option::is_none")]
963 pub arguments: Option<std::collections::HashMap<String, String>>,
964}
965
966#[derive(Debug, Clone, Serialize, Deserialize)]
968#[serde(rename_all = "camelCase")]
969pub struct Completion {
970 pub values: Vec<String>,
972 #[serde(default, skip_serializing_if = "Option::is_none")]
974 pub total: Option<u32>,
975 #[serde(default, skip_serializing_if = "Option::is_none")]
977 pub has_more: Option<bool>,
978}
979
980impl Completion {
981 pub fn new(values: Vec<String>) -> Self {
983 Self {
984 values,
985 total: None,
986 has_more: None,
987 }
988 }
989
990 pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
992 Self {
993 values,
994 total: Some(total),
995 has_more: Some(has_more),
996 }
997 }
998}
999
1000#[derive(Debug, Clone, Serialize, Deserialize)]
1002pub struct CompleteResult {
1003 pub completion: Completion,
1005 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1007 pub meta: Option<Value>,
1008}
1009
1010impl CompleteResult {
1011 pub fn new(values: Vec<String>) -> Self {
1013 Self {
1014 completion: Completion::new(values),
1015 meta: None,
1016 }
1017 }
1018
1019 pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
1021 Self {
1022 completion: Completion::with_pagination(values, total, has_more),
1023 meta: None,
1024 }
1025 }
1026}
1027
1028#[derive(Debug, Clone, Serialize, Deserialize)]
1034pub struct ModelHint {
1035 #[serde(default, skip_serializing_if = "Option::is_none")]
1037 pub name: Option<String>,
1038}
1039
1040impl ModelHint {
1041 pub fn new(name: impl Into<String>) -> Self {
1043 Self {
1044 name: Some(name.into()),
1045 }
1046 }
1047}
1048
1049#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1051#[serde(rename_all = "camelCase")]
1052pub struct ModelPreferences {
1053 #[serde(default, skip_serializing_if = "Option::is_none")]
1055 pub speed_priority: Option<f64>,
1056 #[serde(default, skip_serializing_if = "Option::is_none")]
1058 pub intelligence_priority: Option<f64>,
1059 #[serde(default, skip_serializing_if = "Option::is_none")]
1061 pub cost_priority: Option<f64>,
1062 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1064 pub hints: Vec<ModelHint>,
1065}
1066
1067impl ModelPreferences {
1068 pub fn new() -> Self {
1070 Self::default()
1071 }
1072
1073 pub fn speed(mut self, priority: f64) -> Self {
1075 self.speed_priority = Some(priority.clamp(0.0, 1.0));
1076 self
1077 }
1078
1079 pub fn intelligence(mut self, priority: f64) -> Self {
1081 self.intelligence_priority = Some(priority.clamp(0.0, 1.0));
1082 self
1083 }
1084
1085 pub fn cost(mut self, priority: f64) -> Self {
1087 self.cost_priority = Some(priority.clamp(0.0, 1.0));
1088 self
1089 }
1090
1091 pub fn hint(mut self, name: impl Into<String>) -> Self {
1093 self.hints.push(ModelHint::new(name));
1094 self
1095 }
1096}
1097
1098#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
1100#[serde(rename_all = "camelCase")]
1101#[non_exhaustive]
1102pub enum IncludeContext {
1103 AllServers,
1105 ThisServer,
1107 #[default]
1109 None,
1110}
1111
1112#[derive(Debug, Clone, Serialize, Deserialize)]
1114pub struct SamplingMessage {
1115 pub role: ContentRole,
1117 pub content: SamplingContentOrArray,
1119 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1121 pub meta: Option<Value>,
1122}
1123
1124impl SamplingMessage {
1125 pub fn user(text: impl Into<String>) -> Self {
1127 Self {
1128 role: ContentRole::User,
1129 content: SamplingContentOrArray::Single(SamplingContent::Text {
1130 text: text.into(),
1131 annotations: None,
1132 meta: None,
1133 }),
1134 meta: None,
1135 }
1136 }
1137
1138 pub fn assistant(text: impl Into<String>) -> Self {
1140 Self {
1141 role: ContentRole::Assistant,
1142 content: SamplingContentOrArray::Single(SamplingContent::Text {
1143 text: text.into(),
1144 annotations: None,
1145 meta: None,
1146 }),
1147 meta: None,
1148 }
1149 }
1150}
1151
1152#[derive(Debug, Clone, Serialize, Deserialize)]
1157#[serde(rename_all = "camelCase")]
1158pub struct SamplingTool {
1159 pub name: String,
1161 #[serde(default, skip_serializing_if = "Option::is_none")]
1163 pub title: Option<String>,
1164 #[serde(skip_serializing_if = "Option::is_none")]
1166 pub description: Option<String>,
1167 pub input_schema: Value,
1169 #[serde(default, skip_serializing_if = "Option::is_none")]
1171 pub output_schema: Option<Value>,
1172 #[serde(default, skip_serializing_if = "Option::is_none")]
1174 pub icons: Option<Vec<ToolIcon>>,
1175 #[serde(default, skip_serializing_if = "Option::is_none")]
1177 pub annotations: Option<ToolAnnotations>,
1178 #[serde(default, skip_serializing_if = "Option::is_none")]
1180 pub execution: Option<ToolExecution>,
1181}
1182
1183#[derive(Debug, Clone, Serialize, Deserialize)]
1187pub struct ToolChoice {
1188 pub mode: String,
1190 #[serde(skip_serializing_if = "Option::is_none")]
1192 pub name: Option<String>,
1193}
1194
1195impl ToolChoice {
1196 pub fn auto() -> Self {
1198 Self {
1199 mode: "auto".to_string(),
1200 name: None,
1201 }
1202 }
1203
1204 pub fn required() -> Self {
1206 Self {
1207 mode: "required".to_string(),
1208 name: None,
1209 }
1210 }
1211
1212 pub fn none() -> Self {
1214 Self {
1215 mode: "none".to_string(),
1216 name: None,
1217 }
1218 }
1219
1220 pub fn tool(name: impl Into<String>) -> Self {
1222 Self {
1223 mode: "tool".to_string(),
1224 name: Some(name.into()),
1225 }
1226 }
1227}
1228
1229#[derive(Debug, Clone, Serialize, Deserialize)]
1231#[serde(tag = "type", rename_all = "lowercase")]
1232#[non_exhaustive]
1233pub enum SamplingContent {
1234 Text {
1236 text: String,
1238 #[serde(default, skip_serializing_if = "Option::is_none")]
1240 annotations: Option<ContentAnnotations>,
1241 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1243 meta: Option<Value>,
1244 },
1245 Image {
1247 data: String,
1249 #[serde(rename = "mimeType")]
1251 mime_type: String,
1252 #[serde(default, skip_serializing_if = "Option::is_none")]
1254 annotations: Option<ContentAnnotations>,
1255 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1257 meta: Option<Value>,
1258 },
1259 Audio {
1261 data: String,
1263 #[serde(rename = "mimeType")]
1265 mime_type: String,
1266 #[serde(default, skip_serializing_if = "Option::is_none")]
1268 annotations: Option<ContentAnnotations>,
1269 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1271 meta: Option<Value>,
1272 },
1273 #[serde(rename = "tool_use")]
1275 ToolUse {
1276 id: String,
1278 name: String,
1280 input: Value,
1282 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1284 meta: Option<Value>,
1285 },
1286 #[serde(rename = "tool_result")]
1288 ToolResult {
1289 #[serde(rename = "toolUseId")]
1291 tool_use_id: String,
1292 content: Vec<SamplingContent>,
1294 #[serde(
1296 default,
1297 rename = "structuredContent",
1298 skip_serializing_if = "Option::is_none"
1299 )]
1300 structured_content: Option<Value>,
1301 #[serde(default, rename = "isError", skip_serializing_if = "Option::is_none")]
1303 is_error: Option<bool>,
1304 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1306 meta: Option<Value>,
1307 },
1308}
1309
1310impl SamplingContent {
1311 pub fn as_text(&self) -> Option<&str> {
1332 match self {
1333 SamplingContent::Text { text, .. } => Some(text),
1334 _ => None,
1335 }
1336 }
1337}
1338
1339#[derive(Debug, Clone, Serialize, Deserialize)]
1344#[serde(untagged)]
1345#[non_exhaustive]
1346pub enum SamplingContentOrArray {
1347 Single(SamplingContent),
1349 Array(Vec<SamplingContent>),
1351}
1352
1353impl SamplingContentOrArray {
1354 pub fn items(&self) -> Vec<&SamplingContent> {
1356 match self {
1357 Self::Single(c) => vec![c],
1358 Self::Array(arr) => arr.iter().collect(),
1359 }
1360 }
1361
1362 pub fn into_items(self) -> Vec<SamplingContent> {
1364 match self {
1365 Self::Single(c) => vec![c],
1366 Self::Array(arr) => arr,
1367 }
1368 }
1369}
1370
1371#[derive(Debug, Clone, Serialize, Deserialize)]
1373#[serde(rename_all = "camelCase")]
1374pub struct CreateMessageParams {
1375 pub messages: Vec<SamplingMessage>,
1377 pub max_tokens: u32,
1379 #[serde(default, skip_serializing_if = "Option::is_none")]
1381 pub system_prompt: Option<String>,
1382 #[serde(default, skip_serializing_if = "Option::is_none")]
1384 pub temperature: Option<f64>,
1385 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1387 pub stop_sequences: Vec<String>,
1388 #[serde(default, skip_serializing_if = "Option::is_none")]
1390 pub model_preferences: Option<ModelPreferences>,
1391 #[serde(default, skip_serializing_if = "Option::is_none")]
1393 pub include_context: Option<IncludeContext>,
1394 #[serde(default, skip_serializing_if = "Option::is_none")]
1396 pub metadata: Option<serde_json::Map<String, Value>>,
1397 #[serde(default, skip_serializing_if = "Option::is_none")]
1399 pub tools: Option<Vec<SamplingTool>>,
1400 #[serde(default, skip_serializing_if = "Option::is_none")]
1402 pub tool_choice: Option<ToolChoice>,
1403 #[serde(default, skip_serializing_if = "Option::is_none")]
1405 pub task: Option<TaskRequestParams>,
1406 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1408 pub meta: Option<Value>,
1409}
1410
1411impl CreateMessageParams {
1412 pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
1414 Self {
1415 messages,
1416 max_tokens,
1417 system_prompt: None,
1418 temperature: None,
1419 stop_sequences: Vec::new(),
1420 model_preferences: None,
1421 include_context: None,
1422 metadata: None,
1423 tools: None,
1424 tool_choice: None,
1425 task: None,
1426 meta: None,
1427 }
1428 }
1429
1430 pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
1432 self.system_prompt = Some(prompt.into());
1433 self
1434 }
1435
1436 pub fn temperature(mut self, temp: f64) -> Self {
1438 self.temperature = Some(temp.clamp(0.0, 1.0));
1439 self
1440 }
1441
1442 pub fn stop_sequence(mut self, seq: impl Into<String>) -> Self {
1444 self.stop_sequences.push(seq.into());
1445 self
1446 }
1447
1448 pub fn model_preferences(mut self, prefs: ModelPreferences) -> Self {
1450 self.model_preferences = Some(prefs);
1451 self
1452 }
1453
1454 pub fn include_context(mut self, mode: IncludeContext) -> Self {
1456 self.include_context = Some(mode);
1457 self
1458 }
1459
1460 pub fn tools(mut self, tools: Vec<SamplingTool>) -> Self {
1462 self.tools = Some(tools);
1463 self
1464 }
1465
1466 pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
1468 self.tool_choice = Some(choice);
1469 self
1470 }
1471}
1472
1473#[derive(Debug, Clone, Serialize, Deserialize)]
1475#[serde(rename_all = "camelCase")]
1476pub struct CreateMessageResult {
1477 pub content: SamplingContentOrArray,
1479 pub model: String,
1481 pub role: ContentRole,
1483 #[serde(default, skip_serializing_if = "Option::is_none")]
1485 pub stop_reason: Option<String>,
1486 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1488 pub meta: Option<Value>,
1489}
1490
1491impl CreateMessageResult {
1492 pub fn content_items(&self) -> Vec<&SamplingContent> {
1494 self.content.items()
1495 }
1496
1497 pub fn first_text(&self) -> Option<&str> {
1521 self.content.items().iter().find_map(|c| c.as_text())
1522 }
1523}
1524
1525#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1527#[serde(rename_all = "camelCase")]
1528pub struct Implementation {
1529 pub name: String,
1531 pub version: String,
1533 #[serde(skip_serializing_if = "Option::is_none")]
1535 pub title: Option<String>,
1536 #[serde(skip_serializing_if = "Option::is_none")]
1538 pub description: Option<String>,
1539 #[serde(skip_serializing_if = "Option::is_none")]
1541 pub icons: Option<Vec<ToolIcon>>,
1542 #[serde(skip_serializing_if = "Option::is_none")]
1544 pub website_url: Option<String>,
1545 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1547 pub meta: Option<Value>,
1548}
1549
1550#[derive(Debug, Clone, Serialize, Deserialize)]
1551#[serde(rename_all = "camelCase")]
1552pub struct InitializeResult {
1553 pub protocol_version: String,
1554 pub capabilities: ServerCapabilities,
1555 pub server_info: Implementation,
1556 #[serde(skip_serializing_if = "Option::is_none")]
1559 pub instructions: Option<String>,
1560 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1562 pub meta: Option<Value>,
1563}
1564
1565#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1579pub struct DiscoverParams {}
1580
1581#[derive(Debug, Clone, Serialize, Deserialize)]
1588#[serde(rename_all = "camelCase")]
1589pub struct DiscoverResult {
1590 pub supported_versions: Vec<String>,
1593 pub capabilities: ServerCapabilities,
1595 pub server_info: Implementation,
1597 #[serde(default, skip_serializing_if = "Option::is_none")]
1599 pub instructions: Option<String>,
1600 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1602 pub meta: Option<Value>,
1603}
1604
1605#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1623pub struct MessagesListenParams {
1624 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1626 pub meta: Option<Value>,
1627}
1628
1629#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1635pub struct MessagesListenResult {
1636 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1638 pub meta: Option<Value>,
1639}
1640
1641#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1642#[serde(rename_all = "camelCase")]
1643pub struct ServerCapabilities {
1644 #[serde(default, skip_serializing_if = "Option::is_none")]
1645 pub tools: Option<ToolsCapability>,
1646 #[serde(default, skip_serializing_if = "Option::is_none")]
1647 pub resources: Option<ResourcesCapability>,
1648 #[serde(default, skip_serializing_if = "Option::is_none")]
1649 pub prompts: Option<PromptsCapability>,
1650 #[serde(default, skip_serializing_if = "Option::is_none")]
1652 pub logging: Option<LoggingCapability>,
1653 #[serde(default, skip_serializing_if = "Option::is_none")]
1654 pub tasks: Option<TasksCapability>,
1655 #[serde(default, skip_serializing_if = "Option::is_none")]
1657 pub completions: Option<CompletionsCapability>,
1658 #[serde(default, skip_serializing_if = "Option::is_none")]
1660 pub experimental: Option<HashMap<String, serde_json::Value>>,
1661 #[serde(default, skip_serializing_if = "Option::is_none")]
1663 pub extensions: Option<HashMap<String, serde_json::Value>>,
1664}
1665
1666#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1668pub struct LoggingCapability {
1669 #[serde(default, skip_serializing_if = "Option::is_none")]
1672 pub deprecated: Option<DeprecationInfo>,
1673}
1674
1675#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1677#[serde(rename_all = "camelCase")]
1678pub struct TasksCapability {
1679 #[serde(default, skip_serializing_if = "Option::is_none")]
1681 pub list: Option<TasksListCapability>,
1682 #[serde(default, skip_serializing_if = "Option::is_none")]
1684 pub cancel: Option<TasksCancelCapability>,
1685 #[serde(default, skip_serializing_if = "Option::is_none")]
1687 pub requests: Option<TasksRequestsCapability>,
1688}
1689
1690#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1692pub struct TasksListCapability {}
1693
1694#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1696pub struct TasksCancelCapability {}
1697
1698#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1700#[serde(rename_all = "camelCase")]
1701pub struct TasksRequestsCapability {
1702 #[serde(default, skip_serializing_if = "Option::is_none")]
1704 pub tools: Option<TasksToolsRequestsCapability>,
1705}
1706
1707#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1709#[serde(rename_all = "camelCase")]
1710pub struct TasksToolsRequestsCapability {
1711 #[serde(default, skip_serializing_if = "Option::is_none")]
1713 pub call: Option<TasksToolsCallCapability>,
1714}
1715
1716#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1718pub struct TasksToolsCallCapability {}
1719
1720#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1721#[serde(rename_all = "camelCase")]
1722pub struct ToolsCapability {
1723 #[serde(default)]
1724 pub list_changed: bool,
1725}
1726
1727#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1728#[serde(rename_all = "camelCase")]
1729pub struct ResourcesCapability {
1730 #[serde(default)]
1731 pub subscribe: bool,
1732 #[serde(default)]
1733 pub list_changed: bool,
1734}
1735
1736#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1737#[serde(rename_all = "camelCase")]
1738pub struct PromptsCapability {
1739 #[serde(default)]
1740 pub list_changed: bool,
1741}
1742
1743#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1755#[serde(rename_all = "lowercase")]
1756#[non_exhaustive]
1757pub enum CacheScope {
1758 Session,
1760 Global,
1762}
1763
1764#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
1775#[serde(rename_all = "camelCase")]
1776pub struct DeprecationInfo {
1777 #[serde(default, skip_serializing_if = "Option::is_none")]
1780 pub since: Option<String>,
1781 #[serde(default, skip_serializing_if = "Option::is_none")]
1784 pub remove_in: Option<String>,
1785 #[serde(default, skip_serializing_if = "Option::is_none")]
1787 pub message: Option<String>,
1788 #[serde(default, skip_serializing_if = "Option::is_none")]
1790 pub replacement: Option<String>,
1791}
1792
1793#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1798pub struct ListToolsParams {
1799 #[serde(default, skip_serializing_if = "Option::is_none")]
1800 pub cursor: Option<String>,
1801 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1803 pub meta: Option<RequestMeta>,
1804}
1805
1806#[derive(Debug, Clone, Serialize, Deserialize)]
1807#[serde(rename_all = "camelCase")]
1808pub struct ListToolsResult {
1809 pub tools: Vec<ToolDefinition>,
1810 #[serde(default, skip_serializing_if = "Option::is_none")]
1811 pub next_cursor: Option<String>,
1812 #[serde(default, skip_serializing_if = "Option::is_none")]
1815 pub ttl_ms: Option<u64>,
1816 #[serde(default, skip_serializing_if = "Option::is_none")]
1819 pub cache_scope: Option<CacheScope>,
1820 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1822 pub meta: Option<Value>,
1823}
1824
1825#[derive(Debug, Clone, Serialize, Deserialize)]
1827#[serde(rename_all = "camelCase")]
1828pub struct ToolDefinition {
1829 pub name: String,
1830 #[serde(skip_serializing_if = "Option::is_none")]
1832 pub title: Option<String>,
1833 #[serde(skip_serializing_if = "Option::is_none")]
1834 pub description: Option<String>,
1835 pub input_schema: Value,
1836 #[serde(skip_serializing_if = "Option::is_none")]
1838 pub output_schema: Option<Value>,
1839 #[serde(skip_serializing_if = "Option::is_none")]
1841 pub icons: Option<Vec<ToolIcon>>,
1842 #[serde(skip_serializing_if = "Option::is_none")]
1845 pub annotations: Option<ToolAnnotations>,
1846 #[serde(skip_serializing_if = "Option::is_none")]
1848 pub execution: Option<ToolExecution>,
1849 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1851 pub meta: Option<Value>,
1852}
1853
1854#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1856#[serde(rename_all = "lowercase")]
1857#[non_exhaustive]
1858pub enum IconTheme {
1859 Light,
1861 Dark,
1863}
1864
1865#[derive(Debug, Clone, Serialize, Deserialize)]
1867#[serde(rename_all = "camelCase")]
1868pub struct ToolIcon {
1869 pub src: String,
1871 #[serde(skip_serializing_if = "Option::is_none")]
1873 pub mime_type: Option<String>,
1874 #[serde(skip_serializing_if = "Option::is_none")]
1876 pub sizes: Option<Vec<String>>,
1877 #[serde(skip_serializing_if = "Option::is_none")]
1879 pub theme: Option<IconTheme>,
1880}
1881
1882#[derive(Debug, Clone, Serialize, Deserialize)]
1885#[serde(rename_all = "camelCase")]
1886pub struct ToolAnnotations {
1887 #[serde(skip_serializing_if = "Option::is_none")]
1889 pub title: Option<String>,
1890 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1892 pub read_only_hint: bool,
1893 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1896 pub destructive_hint: bool,
1897 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1900 pub idempotent_hint: bool,
1901 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1903 pub open_world_hint: bool,
1904}
1905
1906impl Default for ToolAnnotations {
1907 fn default() -> Self {
1908 Self {
1909 title: None,
1910 read_only_hint: false,
1911 destructive_hint: true,
1912 idempotent_hint: false,
1913 open_world_hint: true,
1914 }
1915 }
1916}
1917
1918impl ToolAnnotations {
1919 pub fn is_read_only(&self) -> bool {
1921 self.read_only_hint
1922 }
1923
1924 pub fn is_destructive(&self) -> bool {
1926 self.destructive_hint
1927 }
1928
1929 pub fn is_idempotent(&self) -> bool {
1931 self.idempotent_hint
1932 }
1933
1934 pub fn is_open_world(&self) -> bool {
1936 self.open_world_hint
1937 }
1938}
1939
1940impl ToolDefinition {
1941 pub fn is_read_only(&self) -> bool {
1945 self.annotations.as_ref().is_some_and(|a| a.read_only_hint)
1946 }
1947
1948 pub fn is_destructive(&self) -> bool {
1952 self.annotations.as_ref().is_none_or(|a| a.destructive_hint)
1953 }
1954
1955 pub fn is_idempotent(&self) -> bool {
1959 self.annotations.as_ref().is_some_and(|a| a.idempotent_hint)
1960 }
1961
1962 pub fn is_open_world(&self) -> bool {
1966 self.annotations.as_ref().is_none_or(|a| a.open_world_hint)
1967 }
1968}
1969
1970fn default_true() -> bool {
1971 true
1972}
1973
1974fn is_true(v: &bool) -> bool {
1975 *v
1976}
1977
1978#[derive(Debug, Clone, Serialize, Deserialize)]
1979pub struct CallToolParams {
1980 pub name: String,
1981 #[serde(default)]
1982 pub arguments: Value,
1983 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1985 pub meta: Option<RequestMeta>,
1986 #[serde(default, skip_serializing_if = "Option::is_none")]
1988 pub task: Option<TaskRequestParams>,
1989}
1990
1991#[derive(Debug, Clone, Serialize, Deserialize)]
2012#[serde(rename_all = "camelCase")]
2013pub struct CallToolResult {
2014 pub content: Vec<Content>,
2016 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2018 pub is_error: bool,
2019 #[serde(default, skip_serializing_if = "Option::is_none")]
2021 pub structured_content: Option<Value>,
2022 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2024 pub meta: Option<Value>,
2025}
2026
2027impl CallToolResult {
2028 pub fn text(text: impl Into<String>) -> Self {
2032 Self {
2033 content: vec![Content::Text {
2034 text: text.into(),
2035 annotations: None,
2036 meta: None,
2037 }],
2038 is_error: false,
2039 structured_content: None,
2040 meta: None,
2041 }
2042 }
2043
2044 pub fn error(message: impl Into<String>) -> Self {
2049 Self {
2050 content: vec![Content::Text {
2051 text: message.into(),
2052 annotations: None,
2053 meta: None,
2054 }],
2055 is_error: true,
2056 structured_content: None,
2057 meta: None,
2058 }
2059 }
2060
2061 pub fn json(value: Value) -> Self {
2069 let text = serde_json::to_string_pretty(&value).unwrap_or_default();
2070 Self {
2071 content: vec![Content::Text {
2072 text,
2073 annotations: None,
2074 meta: None,
2075 }],
2076 is_error: false,
2077 structured_content: Some(value),
2078 meta: None,
2079 }
2080 }
2081
2082 pub fn from_serialize(
2114 value: &impl serde::Serialize,
2115 ) -> std::result::Result<Self, crate::error::Error> {
2116 let json_value = serde_json::to_value(value)
2117 .map_err(|e| crate::error::Error::tool(format!("Serialization failed: {}", e)))?;
2118 Ok(Self::json(json_value))
2119 }
2120
2121 pub fn from_list<T: serde::Serialize>(
2148 key: &str,
2149 items: &[T],
2150 ) -> std::result::Result<Self, crate::error::Error> {
2151 Self::from_serialize(&serde_json::json!({ key: items, "count": items.len() }))
2152 }
2153
2154 pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
2169 Self {
2170 content: vec![Content::Image {
2171 data: data.into(),
2172 mime_type: mime_type.into(),
2173 annotations: None,
2174 meta: None,
2175 }],
2176 is_error: false,
2177 structured_content: None,
2178 meta: None,
2179 }
2180 }
2181
2182 pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
2197 Self {
2198 content: vec![Content::Audio {
2199 data: data.into(),
2200 mime_type: mime_type.into(),
2201 annotations: None,
2202 meta: None,
2203 }],
2204 is_error: false,
2205 structured_content: None,
2206 meta: None,
2207 }
2208 }
2209
2210 pub fn resource_link(uri: impl Into<String>, name: impl Into<String>) -> Self {
2227 Self {
2228 content: vec![Content::ResourceLink {
2229 uri: uri.into(),
2230 name: name.into(),
2231 title: None,
2232 description: None,
2233 mime_type: None,
2234 size: None,
2235 icons: None,
2236 annotations: None,
2237 meta: None,
2238 }],
2239 is_error: false,
2240 structured_content: None,
2241 meta: None,
2242 }
2243 }
2244
2245 pub fn resource_link_with_meta(
2247 uri: impl Into<String>,
2248 name: impl Into<String>,
2249 description: Option<String>,
2250 mime_type: Option<String>,
2251 ) -> Self {
2252 Self {
2253 content: vec![Content::ResourceLink {
2254 uri: uri.into(),
2255 name: name.into(),
2256 title: None,
2257 description,
2258 mime_type,
2259 size: None,
2260 icons: None,
2261 annotations: None,
2262 meta: None,
2263 }],
2264 is_error: false,
2265 structured_content: None,
2266 meta: None,
2267 }
2268 }
2269
2270 pub fn resource(resource: ResourceContent) -> Self {
2272 Self {
2273 content: vec![Content::Resource {
2274 resource,
2275 annotations: None,
2276 meta: None,
2277 }],
2278 is_error: false,
2279 structured_content: None,
2280 meta: None,
2281 }
2282 }
2283
2284 pub fn all_text(&self) -> String {
2298 self.content.iter().filter_map(|c| c.as_text()).collect()
2299 }
2300
2301 pub fn first_text(&self) -> Option<&str> {
2314 self.content.iter().find_map(|c| c.as_text())
2315 }
2316
2317 pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
2334 if let Some(ref sc) = self.structured_content {
2335 return Some(Ok(sc.clone()));
2336 }
2337 self.first_text().map(serde_json::from_str)
2338 }
2339
2340 pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
2360 if let Some(ref sc) = self.structured_content {
2361 return Some(serde_json::from_value(sc.clone()));
2362 }
2363 self.first_text().map(serde_json::from_str)
2364 }
2365}
2366
2367#[derive(Debug, Clone, Serialize, Deserialize)]
2372#[serde(tag = "type", rename_all = "snake_case")]
2373#[non_exhaustive]
2374pub enum Content {
2375 Text {
2377 text: String,
2379 #[serde(skip_serializing_if = "Option::is_none")]
2381 annotations: Option<ContentAnnotations>,
2382 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2384 meta: Option<Value>,
2385 },
2386 Image {
2388 data: String,
2390 #[serde(rename = "mimeType")]
2392 mime_type: String,
2393 #[serde(skip_serializing_if = "Option::is_none")]
2395 annotations: Option<ContentAnnotations>,
2396 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2398 meta: Option<Value>,
2399 },
2400 Audio {
2402 data: String,
2404 #[serde(rename = "mimeType")]
2406 mime_type: String,
2407 #[serde(skip_serializing_if = "Option::is_none")]
2409 annotations: Option<ContentAnnotations>,
2410 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2412 meta: Option<Value>,
2413 },
2414 Resource {
2416 resource: ResourceContent,
2418 #[serde(skip_serializing_if = "Option::is_none")]
2420 annotations: Option<ContentAnnotations>,
2421 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2423 meta: Option<Value>,
2424 },
2425 ResourceLink {
2427 uri: String,
2429 name: String,
2431 #[serde(default, skip_serializing_if = "Option::is_none")]
2433 title: Option<String>,
2434 #[serde(skip_serializing_if = "Option::is_none")]
2436 description: Option<String>,
2437 #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
2439 mime_type: Option<String>,
2440 #[serde(default, skip_serializing_if = "Option::is_none")]
2442 size: Option<u64>,
2443 #[serde(default, skip_serializing_if = "Option::is_none")]
2445 icons: Option<Vec<ToolIcon>>,
2446 #[serde(skip_serializing_if = "Option::is_none")]
2447 annotations: Option<ContentAnnotations>,
2448 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2450 meta: Option<Value>,
2451 },
2452}
2453
2454#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2456pub struct ContentAnnotations {
2457 #[serde(skip_serializing_if = "Option::is_none")]
2459 pub audience: Option<Vec<ContentRole>>,
2460 #[serde(skip_serializing_if = "Option::is_none")]
2462 pub priority: Option<f64>,
2463 #[serde(rename = "lastModified", skip_serializing_if = "Option::is_none")]
2465 pub last_modified: Option<String>,
2466}
2467
2468impl Content {
2469 pub fn text(text: impl Into<String>) -> Self {
2495 Content::Text {
2496 text: text.into(),
2497 annotations: None,
2498 meta: None,
2499 }
2500 }
2501
2502 pub fn as_text(&self) -> Option<&str> {
2515 match self {
2516 Content::Text { text, .. } => Some(text),
2517 _ => None,
2518 }
2519 }
2520}
2521
2522#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2526#[serde(rename_all = "lowercase")]
2527#[non_exhaustive]
2528pub enum ContentRole {
2529 User,
2531 Assistant,
2533}
2534
2535#[derive(Debug, Clone, Serialize, Deserialize)]
2539#[serde(rename_all = "camelCase")]
2540pub struct ResourceContent {
2541 pub uri: String,
2543 #[serde(skip_serializing_if = "Option::is_none")]
2545 pub mime_type: Option<String>,
2546 #[serde(skip_serializing_if = "Option::is_none")]
2548 pub text: Option<String>,
2549 #[serde(skip_serializing_if = "Option::is_none")]
2551 pub blob: Option<String>,
2552 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2554 pub meta: Option<Value>,
2555}
2556
2557#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2562pub struct ListResourcesParams {
2563 #[serde(default, skip_serializing_if = "Option::is_none")]
2564 pub cursor: Option<String>,
2565 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2567 pub meta: Option<RequestMeta>,
2568}
2569
2570#[derive(Debug, Clone, Serialize, Deserialize)]
2571#[serde(rename_all = "camelCase")]
2572pub struct ListResourcesResult {
2573 pub resources: Vec<ResourceDefinition>,
2574 #[serde(default, skip_serializing_if = "Option::is_none")]
2575 pub next_cursor: Option<String>,
2576 #[serde(default, skip_serializing_if = "Option::is_none")]
2578 pub ttl_ms: Option<u64>,
2579 #[serde(default, skip_serializing_if = "Option::is_none")]
2581 pub cache_scope: Option<CacheScope>,
2582 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2584 pub meta: Option<Value>,
2585}
2586
2587#[derive(Debug, Clone, Serialize, Deserialize)]
2588#[serde(rename_all = "camelCase")]
2589pub struct ResourceDefinition {
2590 pub uri: String,
2591 pub name: String,
2592 #[serde(skip_serializing_if = "Option::is_none")]
2594 pub title: Option<String>,
2595 #[serde(skip_serializing_if = "Option::is_none")]
2596 pub description: Option<String>,
2597 #[serde(skip_serializing_if = "Option::is_none")]
2598 pub mime_type: Option<String>,
2599 #[serde(skip_serializing_if = "Option::is_none")]
2601 pub icons: Option<Vec<ToolIcon>>,
2602 #[serde(skip_serializing_if = "Option::is_none")]
2604 pub size: Option<u64>,
2605 #[serde(skip_serializing_if = "Option::is_none")]
2607 pub annotations: Option<ContentAnnotations>,
2608 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2610 pub meta: Option<Value>,
2611}
2612
2613#[derive(Debug, Clone, Serialize, Deserialize)]
2614pub struct ReadResourceParams {
2615 pub uri: String,
2616 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2618 pub meta: Option<RequestMeta>,
2619}
2620
2621#[derive(Debug, Clone, Serialize, Deserialize)]
2622pub struct ReadResourceResult {
2623 pub contents: Vec<ResourceContent>,
2624 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2626 pub meta: Option<Value>,
2627}
2628
2629impl ReadResourceResult {
2630 pub fn text(uri: impl Into<String>, content: impl Into<String>) -> Self {
2640 Self {
2641 contents: vec![ResourceContent {
2642 uri: uri.into(),
2643 mime_type: Some("text/plain".to_string()),
2644 text: Some(content.into()),
2645 blob: None,
2646 meta: None,
2647 }],
2648 meta: None,
2649 }
2650 }
2651
2652 pub fn text_with_mime(
2666 uri: impl Into<String>,
2667 content: impl Into<String>,
2668 mime_type: impl Into<String>,
2669 ) -> Self {
2670 Self {
2671 contents: vec![ResourceContent {
2672 uri: uri.into(),
2673 mime_type: Some(mime_type.into()),
2674 text: Some(content.into()),
2675 blob: None,
2676 meta: None,
2677 }],
2678 meta: None,
2679 }
2680 }
2681
2682 pub fn json<T: serde::Serialize>(uri: impl Into<String>, value: &T) -> Self {
2696 let json_string =
2697 serde_json::to_string_pretty(value).unwrap_or_else(|_| "null".to_string());
2698 Self {
2699 contents: vec![ResourceContent {
2700 uri: uri.into(),
2701 mime_type: Some("application/json".to_string()),
2702 text: Some(json_string),
2703 blob: None,
2704 meta: None,
2705 }],
2706 meta: None,
2707 }
2708 }
2709
2710 pub fn blob(uri: impl Into<String>, bytes: &[u8]) -> Self {
2721 use base64::Engine;
2722 let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
2723 Self {
2724 contents: vec![ResourceContent {
2725 uri: uri.into(),
2726 mime_type: Some("application/octet-stream".to_string()),
2727 text: None,
2728 blob: Some(encoded),
2729 meta: None,
2730 }],
2731 meta: None,
2732 }
2733 }
2734
2735 pub fn blob_with_mime(
2746 uri: impl Into<String>,
2747 bytes: &[u8],
2748 mime_type: impl Into<String>,
2749 ) -> Self {
2750 use base64::Engine;
2751 let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
2752 Self {
2753 contents: vec![ResourceContent {
2754 uri: uri.into(),
2755 mime_type: Some(mime_type.into()),
2756 text: None,
2757 blob: Some(encoded),
2758 meta: None,
2759 }],
2760 meta: None,
2761 }
2762 }
2763
2764 pub fn first_text(&self) -> Option<&str> {
2777 self.contents.first().and_then(|c| c.text.as_deref())
2778 }
2779
2780 pub fn first_uri(&self) -> Option<&str> {
2793 self.contents.first().map(|c| c.uri.as_str())
2794 }
2795
2796 pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
2811 self.first_text().map(serde_json::from_str)
2812 }
2813
2814 pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
2833 self.first_text().map(serde_json::from_str)
2834 }
2835}
2836
2837#[derive(Debug, Clone, Deserialize)]
2838pub struct SubscribeResourceParams {
2839 pub uri: String,
2840 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2842 pub meta: Option<RequestMeta>,
2843}
2844
2845#[derive(Debug, Clone, Deserialize)]
2846pub struct UnsubscribeResourceParams {
2847 pub uri: String,
2848 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2850 pub meta: Option<RequestMeta>,
2851}
2852
2853#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2855pub struct ListResourceTemplatesParams {
2856 #[serde(default)]
2858 pub cursor: Option<String>,
2859 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2861 pub meta: Option<RequestMeta>,
2862}
2863
2864#[derive(Debug, Clone, Serialize, Deserialize)]
2866#[serde(rename_all = "camelCase")]
2867pub struct ListResourceTemplatesResult {
2868 pub resource_templates: Vec<ResourceTemplateDefinition>,
2870 #[serde(skip_serializing_if = "Option::is_none")]
2872 pub next_cursor: Option<String>,
2873 #[serde(default, skip_serializing_if = "Option::is_none")]
2875 pub ttl_ms: Option<u64>,
2876 #[serde(default, skip_serializing_if = "Option::is_none")]
2878 pub cache_scope: Option<CacheScope>,
2879 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2881 pub meta: Option<Value>,
2882}
2883
2884#[derive(Debug, Clone, Serialize, Deserialize)]
2900#[serde(rename_all = "camelCase")]
2901pub struct ResourceTemplateDefinition {
2902 pub uri_template: String,
2904 pub name: String,
2906 #[serde(skip_serializing_if = "Option::is_none")]
2908 pub title: Option<String>,
2909 #[serde(skip_serializing_if = "Option::is_none")]
2911 pub description: Option<String>,
2912 #[serde(skip_serializing_if = "Option::is_none")]
2914 pub mime_type: Option<String>,
2915 #[serde(skip_serializing_if = "Option::is_none")]
2917 pub icons: Option<Vec<ToolIcon>>,
2918 #[serde(skip_serializing_if = "Option::is_none")]
2920 pub annotations: Option<ContentAnnotations>,
2921 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2923 pub arguments: Vec<PromptArgument>,
2924 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2926 pub meta: Option<Value>,
2927}
2928
2929#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2934pub struct ListPromptsParams {
2935 #[serde(default, skip_serializing_if = "Option::is_none")]
2936 pub cursor: Option<String>,
2937 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2939 pub meta: Option<RequestMeta>,
2940}
2941
2942#[derive(Debug, Clone, Serialize, Deserialize)]
2943#[serde(rename_all = "camelCase")]
2944pub struct ListPromptsResult {
2945 pub prompts: Vec<PromptDefinition>,
2946 #[serde(default, skip_serializing_if = "Option::is_none")]
2947 pub next_cursor: Option<String>,
2948 #[serde(default, skip_serializing_if = "Option::is_none")]
2950 pub ttl_ms: Option<u64>,
2951 #[serde(default, skip_serializing_if = "Option::is_none")]
2953 pub cache_scope: Option<CacheScope>,
2954 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2956 pub meta: Option<Value>,
2957}
2958
2959#[derive(Debug, Clone, Serialize, Deserialize)]
2960pub struct PromptDefinition {
2961 pub name: String,
2962 #[serde(skip_serializing_if = "Option::is_none")]
2964 pub title: Option<String>,
2965 #[serde(skip_serializing_if = "Option::is_none")]
2966 pub description: Option<String>,
2967 #[serde(skip_serializing_if = "Option::is_none")]
2969 pub icons: Option<Vec<ToolIcon>>,
2970 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2971 pub arguments: Vec<PromptArgument>,
2972 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2974 pub meta: Option<Value>,
2975}
2976
2977#[derive(Debug, Clone, Serialize, Deserialize)]
2978pub struct PromptArgument {
2979 pub name: String,
2980 #[serde(skip_serializing_if = "Option::is_none")]
2981 pub description: Option<String>,
2982 #[serde(default)]
2983 pub required: bool,
2984}
2985
2986#[derive(Debug, Clone, Serialize, Deserialize)]
2987pub struct GetPromptParams {
2988 pub name: String,
2989 #[serde(default)]
2990 pub arguments: std::collections::HashMap<String, String>,
2991 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2993 pub meta: Option<RequestMeta>,
2994}
2995
2996#[derive(Debug, Clone, Serialize, Deserialize)]
2997pub struct GetPromptResult {
3062 #[serde(default, skip_serializing_if = "Option::is_none")]
3063 pub description: Option<String>,
3064 pub messages: Vec<PromptMessage>,
3065 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3067 pub meta: Option<Value>,
3068}
3069
3070impl GetPromptResult {
3071 pub fn user_message(text: impl Into<String>) -> Self {
3081 Self {
3082 description: None,
3083 messages: vec![PromptMessage {
3084 role: PromptRole::User,
3085 content: Content::Text {
3086 text: text.into(),
3087 annotations: None,
3088 meta: None,
3089 },
3090 meta: None,
3091 }],
3092 meta: None,
3093 }
3094 }
3095
3096 pub fn user_message_with_description(
3109 text: impl Into<String>,
3110 description: impl Into<String>,
3111 ) -> Self {
3112 Self {
3113 description: Some(description.into()),
3114 messages: vec![PromptMessage {
3115 role: PromptRole::User,
3116 content: Content::Text {
3117 text: text.into(),
3118 annotations: None,
3119 meta: None,
3120 },
3121 meta: None,
3122 }],
3123 meta: None,
3124 }
3125 }
3126
3127 pub fn assistant_message(text: impl Into<String>) -> Self {
3137 Self {
3138 description: None,
3139 messages: vec![PromptMessage {
3140 role: PromptRole::Assistant,
3141 content: Content::Text {
3142 text: text.into(),
3143 annotations: None,
3144 meta: None,
3145 },
3146 meta: None,
3147 }],
3148 meta: None,
3149 }
3150 }
3151
3152 pub fn builder() -> GetPromptResultBuilder {
3167 GetPromptResultBuilder::new()
3168 }
3169
3170 pub fn first_message_text(&self) -> Option<&str> {
3184 self.messages.first().and_then(|m| m.content.as_text())
3185 }
3186
3187 pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
3202 self.first_message_text().map(serde_json::from_str)
3203 }
3204
3205 pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
3224 self.first_message_text().map(serde_json::from_str)
3225 }
3226}
3227
3228#[derive(Debug, Clone, Default)]
3230pub struct GetPromptResultBuilder {
3231 description: Option<String>,
3232 messages: Vec<PromptMessage>,
3233}
3234
3235impl GetPromptResultBuilder {
3236 pub fn new() -> Self {
3238 Self::default()
3239 }
3240
3241 pub fn description(mut self, description: impl Into<String>) -> Self {
3243 self.description = Some(description.into());
3244 self
3245 }
3246
3247 pub fn user(mut self, text: impl Into<String>) -> Self {
3249 self.messages.push(PromptMessage {
3250 role: PromptRole::User,
3251 content: Content::Text {
3252 text: text.into(),
3253 annotations: None,
3254 meta: None,
3255 },
3256 meta: None,
3257 });
3258 self
3259 }
3260
3261 pub fn assistant(mut self, text: impl Into<String>) -> Self {
3263 self.messages.push(PromptMessage {
3264 role: PromptRole::Assistant,
3265 content: Content::Text {
3266 text: text.into(),
3267 annotations: None,
3268 meta: None,
3269 },
3270 meta: None,
3271 });
3272 self
3273 }
3274
3275 pub fn build(self) -> GetPromptResult {
3277 GetPromptResult {
3278 description: self.description,
3279 messages: self.messages,
3280 meta: None,
3281 }
3282 }
3283}
3284
3285#[derive(Debug, Clone, Serialize, Deserialize)]
3286pub struct PromptMessage {
3287 pub role: PromptRole,
3288 pub content: Content,
3289 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3291 pub meta: Option<Value>,
3292}
3293
3294#[derive(Debug, Clone, Serialize, Deserialize)]
3295#[serde(rename_all = "lowercase")]
3296#[non_exhaustive]
3297pub enum PromptRole {
3298 User,
3299 Assistant,
3300}
3301
3302pub const TASKS_EXTENSION_ID: &str = "io.modelcontextprotocol/tasks";
3313
3314pub const RESULT_TYPE_TASK: &str = "task";
3320
3321#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
3323#[serde(rename_all = "camelCase")]
3324#[non_exhaustive]
3325pub enum TaskSupportMode {
3326 Required,
3328 Optional,
3330 #[default]
3332 Forbidden,
3333}
3334
3335#[derive(Debug, Clone, Serialize, Deserialize)]
3337#[serde(rename_all = "camelCase")]
3338pub struct ToolExecution {
3339 #[serde(default, skip_serializing_if = "Option::is_none")]
3341 pub task_support: Option<TaskSupportMode>,
3342}
3343
3344#[derive(Debug, Clone, Serialize, Deserialize)]
3346#[serde(rename_all = "camelCase")]
3347pub struct TaskRequestParams {
3348 #[serde(default, skip_serializing_if = "Option::is_none")]
3350 pub ttl: Option<u64>,
3351}
3352
3353#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3355#[serde(rename_all = "snake_case")]
3356#[non_exhaustive]
3357pub enum TaskStatus {
3358 Working,
3360 InputRequired,
3362 Completed,
3364 Failed,
3366 Cancelled,
3368}
3369
3370impl std::fmt::Display for TaskStatus {
3371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3372 match self {
3373 TaskStatus::Working => write!(f, "working"),
3374 TaskStatus::InputRequired => write!(f, "input_required"),
3375 TaskStatus::Completed => write!(f, "completed"),
3376 TaskStatus::Failed => write!(f, "failed"),
3377 TaskStatus::Cancelled => write!(f, "cancelled"),
3378 }
3379 }
3380}
3381
3382impl TaskStatus {
3383 pub fn is_terminal(&self) -> bool {
3385 matches!(
3386 self,
3387 TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
3388 )
3389 }
3390}
3391
3392#[derive(Debug, Clone, Serialize, Deserialize)]
3394#[serde(rename_all = "camelCase")]
3395pub struct TaskObject {
3396 pub task_id: String,
3398 pub status: TaskStatus,
3400 #[serde(skip_serializing_if = "Option::is_none")]
3402 pub status_message: Option<String>,
3403 pub created_at: String,
3405 pub last_updated_at: String,
3407 pub ttl: Option<u64>,
3409 #[serde(skip_serializing_if = "Option::is_none")]
3411 pub poll_interval: Option<u64>,
3412 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3414 pub meta: Option<Value>,
3415}
3416
3417#[deprecated(note = "Use TaskObject instead")]
3419pub type TaskInfo = TaskObject;
3420
3421#[derive(Debug, Clone)]
3446pub struct CreateTaskResult {
3447 pub task: TaskObject,
3450 pub meta: Option<Value>,
3452}
3453
3454impl CreateTaskResult {
3455 pub const RESULT_TYPE: &'static str = RESULT_TYPE_TASK;
3457
3458 pub fn new(task: TaskObject) -> Self {
3461 Self { task, meta: None }
3462 }
3463}
3464
3465impl Serialize for CreateTaskResult {
3466 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3467 where
3468 S: serde::Serializer,
3469 {
3470 let mut value = serde_json::to_value(&self.task)
3473 .map_err(|e| serde::ser::Error::custom(format!("task serialize: {e}")))?;
3474 let obj = value
3475 .as_object_mut()
3476 .ok_or_else(|| serde::ser::Error::custom("task did not serialize to a JSON object"))?;
3477 obj.insert(
3478 "resultType".to_string(),
3479 Value::String(Self::RESULT_TYPE.to_string()),
3480 );
3481 obj.insert(
3482 "task".to_string(),
3483 serde_json::to_value(&self.task)
3484 .map_err(|e| serde::ser::Error::custom(format!("task mirror: {e}")))?,
3485 );
3486 if let Some(meta) = &self.meta {
3487 obj.insert("_meta".to_string(), meta.clone());
3488 }
3489 value.serialize(serializer)
3490 }
3491}
3492
3493impl<'de> Deserialize<'de> for CreateTaskResult {
3494 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3495 where
3496 D: serde::Deserializer<'de>,
3497 {
3498 let value = Value::deserialize(deserializer)?;
3502 let meta = value.get("_meta").filter(|v| !v.is_null()).cloned();
3503 if let Some(task_val) = value.get("task")
3504 && task_val.is_object()
3505 && task_val
3506 .as_object()
3507 .is_some_and(|o| o.contains_key("taskId"))
3508 {
3509 let task: TaskObject =
3511 serde_json::from_value(task_val.clone()).map_err(serde::de::Error::custom)?;
3512 return Ok(CreateTaskResult { task, meta });
3513 }
3514 let task: TaskObject = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
3516 Ok(CreateTaskResult { task, meta })
3517 }
3518}
3519
3520#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3522#[serde(rename_all = "camelCase")]
3523pub struct ListTasksParams {
3524 #[serde(default)]
3526 pub status: Option<TaskStatus>,
3527 #[serde(default)]
3529 pub cursor: Option<String>,
3530 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3532 pub meta: Option<RequestMeta>,
3533}
3534
3535#[derive(Debug, Clone, Serialize, Deserialize)]
3537#[serde(rename_all = "camelCase")]
3538pub struct ListTasksResult {
3539 pub tasks: Vec<TaskObject>,
3541 #[serde(skip_serializing_if = "Option::is_none")]
3543 pub next_cursor: Option<String>,
3544}
3545
3546#[derive(Debug, Clone, Serialize, Deserialize)]
3548#[serde(rename_all = "camelCase")]
3549pub struct GetTaskInfoParams {
3550 pub task_id: String,
3552 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3554 pub meta: Option<RequestMeta>,
3555}
3556
3557#[derive(Debug, Clone, Serialize, Deserialize)]
3559#[serde(rename_all = "camelCase")]
3560pub struct GetTaskResultParams {
3561 pub task_id: String,
3563 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3565 pub meta: Option<RequestMeta>,
3566}
3567
3568#[derive(Debug, Clone, Serialize, Deserialize)]
3570#[serde(rename_all = "camelCase")]
3571pub struct CancelTaskParams {
3572 pub task_id: String,
3574 #[serde(default)]
3576 pub reason: Option<String>,
3577 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3579 pub meta: Option<RequestMeta>,
3580}
3581
3582#[derive(Debug, Clone, Serialize, Deserialize)]
3591#[serde(rename_all = "camelCase")]
3592pub struct UpdateTaskParams {
3593 pub task_id: String,
3595 #[serde(default)]
3600 pub input_responses: HashMap<String, Value>,
3601 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3603 pub meta: Option<RequestMeta>,
3604}
3605
3606#[derive(Debug, Clone, Serialize, Deserialize)]
3611#[serde(rename_all = "camelCase")]
3612pub struct TaskStatusParams {
3613 pub task_id: String,
3615 pub status: TaskStatus,
3617 #[serde(skip_serializing_if = "Option::is_none")]
3619 pub status_message: Option<String>,
3620 pub created_at: String,
3622 pub last_updated_at: String,
3624 pub ttl: Option<u64>,
3626 #[serde(skip_serializing_if = "Option::is_none")]
3628 pub poll_interval: Option<u64>,
3629 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3631 pub meta: Option<Value>,
3632}
3633
3634pub type TaskStatusChangedParams = TaskStatusParams;
3636
3637#[derive(Debug, Clone, Serialize, Deserialize)]
3643#[serde(rename_all = "camelCase")]
3644pub struct ElicitFormParams {
3645 #[serde(default, skip_serializing_if = "Option::is_none")]
3647 pub mode: Option<ElicitMode>,
3648 pub message: String,
3650 pub requested_schema: ElicitFormSchema,
3652 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3654 pub meta: Option<RequestMeta>,
3655}
3656
3657#[derive(Debug, Clone, Serialize, Deserialize)]
3678#[serde(rename_all = "camelCase")]
3679pub struct ElicitUrlParams {
3680 #[serde(default, skip_serializing_if = "Option::is_none")]
3682 pub mode: Option<ElicitMode>,
3683 pub elicitation_id: String,
3685 pub message: String,
3687 pub url: String,
3689 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3691 pub meta: Option<RequestMeta>,
3692}
3693
3694#[derive(Debug, Clone, Serialize, Deserialize)]
3696#[serde(untagged)]
3697#[non_exhaustive]
3698pub enum ElicitRequestParams {
3699 Form(ElicitFormParams),
3700 Url(ElicitUrlParams),
3701}
3702
3703#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3705#[serde(rename_all = "lowercase")]
3706#[non_exhaustive]
3707pub enum ElicitMode {
3708 Form,
3710 Url,
3712}
3713
3714#[derive(Debug, Clone, Serialize, Deserialize)]
3754pub struct ElicitFormSchema {
3755 #[serde(rename = "type")]
3757 pub schema_type: String,
3758 pub properties: std::collections::HashMap<String, PrimitiveSchemaDefinition>,
3760 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3762 pub required: Vec<String>,
3763}
3764
3765impl ElicitFormSchema {
3766 pub fn new() -> Self {
3768 Self {
3769 schema_type: "object".to_string(),
3770 properties: std::collections::HashMap::new(),
3771 required: Vec::new(),
3772 }
3773 }
3774
3775 pub fn string_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
3777 self.properties.insert(
3778 name.to_string(),
3779 PrimitiveSchemaDefinition::String(StringSchema {
3780 schema_type: "string".to_string(),
3781 title: None,
3782 description: description.map(|s| s.to_string()),
3783 format: None,
3784 pattern: None,
3785 min_length: None,
3786 max_length: None,
3787 default: None,
3788 }),
3789 );
3790 if required {
3791 self.required.push(name.to_string());
3792 }
3793 self
3794 }
3795
3796 pub fn string_field_with_default(
3798 mut self,
3799 name: &str,
3800 description: Option<&str>,
3801 required: bool,
3802 default: &str,
3803 ) -> Self {
3804 self.properties.insert(
3805 name.to_string(),
3806 PrimitiveSchemaDefinition::String(StringSchema {
3807 schema_type: "string".to_string(),
3808 title: None,
3809 description: description.map(|s| s.to_string()),
3810 format: None,
3811 pattern: None,
3812 min_length: None,
3813 max_length: None,
3814 default: Some(default.to_string()),
3815 }),
3816 );
3817 if required {
3818 self.required.push(name.to_string());
3819 }
3820 self
3821 }
3822
3823 pub fn integer_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
3825 self.properties.insert(
3826 name.to_string(),
3827 PrimitiveSchemaDefinition::Integer(IntegerSchema {
3828 schema_type: "integer".to_string(),
3829 title: None,
3830 description: description.map(|s| s.to_string()),
3831 minimum: None,
3832 maximum: None,
3833 default: None,
3834 }),
3835 );
3836 if required {
3837 self.required.push(name.to_string());
3838 }
3839 self
3840 }
3841
3842 pub fn integer_field_with_default(
3844 mut self,
3845 name: &str,
3846 description: Option<&str>,
3847 required: bool,
3848 default: i64,
3849 ) -> Self {
3850 self.properties.insert(
3851 name.to_string(),
3852 PrimitiveSchemaDefinition::Integer(IntegerSchema {
3853 schema_type: "integer".to_string(),
3854 title: None,
3855 description: description.map(|s| s.to_string()),
3856 minimum: None,
3857 maximum: None,
3858 default: Some(default),
3859 }),
3860 );
3861 if required {
3862 self.required.push(name.to_string());
3863 }
3864 self
3865 }
3866
3867 pub fn number_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
3869 self.properties.insert(
3870 name.to_string(),
3871 PrimitiveSchemaDefinition::Number(NumberSchema {
3872 schema_type: "number".to_string(),
3873 title: None,
3874 description: description.map(|s| s.to_string()),
3875 minimum: None,
3876 maximum: None,
3877 default: None,
3878 }),
3879 );
3880 if required {
3881 self.required.push(name.to_string());
3882 }
3883 self
3884 }
3885
3886 pub fn number_field_with_default(
3888 mut self,
3889 name: &str,
3890 description: Option<&str>,
3891 required: bool,
3892 default: f64,
3893 ) -> Self {
3894 self.properties.insert(
3895 name.to_string(),
3896 PrimitiveSchemaDefinition::Number(NumberSchema {
3897 schema_type: "number".to_string(),
3898 title: None,
3899 description: description.map(|s| s.to_string()),
3900 minimum: None,
3901 maximum: None,
3902 default: Some(default),
3903 }),
3904 );
3905 if required {
3906 self.required.push(name.to_string());
3907 }
3908 self
3909 }
3910
3911 pub fn boolean_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
3913 self.properties.insert(
3914 name.to_string(),
3915 PrimitiveSchemaDefinition::Boolean(BooleanSchema {
3916 schema_type: "boolean".to_string(),
3917 title: None,
3918 description: description.map(|s| s.to_string()),
3919 default: None,
3920 }),
3921 );
3922 if required {
3923 self.required.push(name.to_string());
3924 }
3925 self
3926 }
3927
3928 pub fn boolean_field_with_default(
3930 mut self,
3931 name: &str,
3932 description: Option<&str>,
3933 required: bool,
3934 default: bool,
3935 ) -> Self {
3936 self.properties.insert(
3937 name.to_string(),
3938 PrimitiveSchemaDefinition::Boolean(BooleanSchema {
3939 schema_type: "boolean".to_string(),
3940 title: None,
3941 description: description.map(|s| s.to_string()),
3942 default: Some(default),
3943 }),
3944 );
3945 if required {
3946 self.required.push(name.to_string());
3947 }
3948 self
3949 }
3950
3951 pub fn enum_field(
3953 mut self,
3954 name: &str,
3955 description: Option<&str>,
3956 options: Vec<String>,
3957 required: bool,
3958 ) -> Self {
3959 self.properties.insert(
3960 name.to_string(),
3961 PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
3962 schema_type: "string".to_string(),
3963 title: None,
3964 description: description.map(|s| s.to_string()),
3965 enum_values: options,
3966 default: None,
3967 }),
3968 );
3969 if required {
3970 self.required.push(name.to_string());
3971 }
3972 self
3973 }
3974
3975 pub fn enum_field_with_default(
3977 mut self,
3978 name: &str,
3979 description: Option<&str>,
3980 required: bool,
3981 options: &[&str],
3982 default: &str,
3983 ) -> Self {
3984 self.properties.insert(
3985 name.to_string(),
3986 PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
3987 schema_type: "string".to_string(),
3988 title: None,
3989 description: description.map(|s| s.to_string()),
3990 enum_values: options.iter().map(|s| s.to_string()).collect(),
3991 default: Some(default.to_string()),
3992 }),
3993 );
3994 if required {
3995 self.required.push(name.to_string());
3996 }
3997 self
3998 }
3999
4000 pub fn raw_field(mut self, name: &str, schema: serde_json::Value, required: bool) -> Self {
4004 self.properties
4005 .insert(name.to_string(), PrimitiveSchemaDefinition::Raw(schema));
4006 if required {
4007 self.required.push(name.to_string());
4008 }
4009 self
4010 }
4011}
4012
4013impl Default for ElicitFormSchema {
4014 fn default() -> Self {
4015 Self::new()
4016 }
4017}
4018
4019#[derive(Debug, Clone, Serialize, Deserialize)]
4021#[serde(untagged)]
4022#[non_exhaustive]
4023pub enum PrimitiveSchemaDefinition {
4024 String(StringSchema),
4026 Integer(IntegerSchema),
4028 Number(NumberSchema),
4030 Boolean(BooleanSchema),
4032 SingleSelectEnum(SingleSelectEnumSchema),
4034 MultiSelectEnum(MultiSelectEnumSchema),
4036 Raw(serde_json::Value),
4038}
4039
4040#[derive(Debug, Clone, Serialize, Deserialize)]
4042#[serde(rename_all = "camelCase")]
4043pub struct StringSchema {
4044 #[serde(rename = "type")]
4045 pub schema_type: String,
4046 #[serde(skip_serializing_if = "Option::is_none")]
4048 pub title: Option<String>,
4049 #[serde(skip_serializing_if = "Option::is_none")]
4050 pub description: Option<String>,
4051 #[serde(skip_serializing_if = "Option::is_none")]
4052 pub format: Option<String>,
4053 #[serde(skip_serializing_if = "Option::is_none")]
4055 pub pattern: Option<String>,
4056 #[serde(skip_serializing_if = "Option::is_none")]
4057 pub min_length: Option<u64>,
4058 #[serde(skip_serializing_if = "Option::is_none")]
4059 pub max_length: Option<u64>,
4060 #[serde(skip_serializing_if = "Option::is_none")]
4062 pub default: Option<String>,
4063}
4064
4065#[derive(Debug, Clone, Serialize, Deserialize)]
4067#[serde(rename_all = "camelCase")]
4068pub struct IntegerSchema {
4069 #[serde(rename = "type")]
4070 pub schema_type: String,
4071 #[serde(skip_serializing_if = "Option::is_none")]
4073 pub title: Option<String>,
4074 #[serde(skip_serializing_if = "Option::is_none")]
4075 pub description: Option<String>,
4076 #[serde(skip_serializing_if = "Option::is_none")]
4077 pub minimum: Option<i64>,
4078 #[serde(skip_serializing_if = "Option::is_none")]
4079 pub maximum: Option<i64>,
4080 #[serde(skip_serializing_if = "Option::is_none")]
4082 pub default: Option<i64>,
4083}
4084
4085#[derive(Debug, Clone, Serialize, Deserialize)]
4087#[serde(rename_all = "camelCase")]
4088pub struct NumberSchema {
4089 #[serde(rename = "type")]
4090 pub schema_type: String,
4091 #[serde(skip_serializing_if = "Option::is_none")]
4093 pub title: Option<String>,
4094 #[serde(skip_serializing_if = "Option::is_none")]
4095 pub description: Option<String>,
4096 #[serde(skip_serializing_if = "Option::is_none")]
4097 pub minimum: Option<f64>,
4098 #[serde(skip_serializing_if = "Option::is_none")]
4099 pub maximum: Option<f64>,
4100 #[serde(skip_serializing_if = "Option::is_none")]
4102 pub default: Option<f64>,
4103}
4104
4105#[derive(Debug, Clone, Serialize, Deserialize)]
4107#[serde(rename_all = "camelCase")]
4108pub struct BooleanSchema {
4109 #[serde(rename = "type")]
4110 pub schema_type: String,
4111 #[serde(skip_serializing_if = "Option::is_none")]
4113 pub title: Option<String>,
4114 #[serde(skip_serializing_if = "Option::is_none")]
4115 pub description: Option<String>,
4116 #[serde(skip_serializing_if = "Option::is_none")]
4118 pub default: Option<bool>,
4119}
4120
4121#[derive(Debug, Clone, Serialize, Deserialize)]
4123#[serde(rename_all = "camelCase")]
4124pub struct SingleSelectEnumSchema {
4125 #[serde(rename = "type")]
4126 pub schema_type: String,
4127 #[serde(skip_serializing_if = "Option::is_none")]
4129 pub title: Option<String>,
4130 #[serde(skip_serializing_if = "Option::is_none")]
4131 pub description: Option<String>,
4132 #[serde(rename = "enum")]
4133 pub enum_values: Vec<String>,
4134 #[serde(skip_serializing_if = "Option::is_none")]
4136 pub default: Option<String>,
4137}
4138
4139#[derive(Debug, Clone, Serialize, Deserialize)]
4141#[serde(rename_all = "camelCase")]
4142pub struct MultiSelectEnumSchema {
4143 #[serde(rename = "type")]
4144 pub schema_type: String,
4145 #[serde(skip_serializing_if = "Option::is_none")]
4147 pub title: Option<String>,
4148 #[serde(skip_serializing_if = "Option::is_none")]
4149 pub description: Option<String>,
4150 pub items: MultiSelectEnumItems,
4151 #[serde(skip_serializing_if = "Option::is_none")]
4152 pub unique_items: Option<bool>,
4153 #[serde(skip_serializing_if = "Option::is_none")]
4155 pub default: Option<Vec<String>>,
4156}
4157
4158#[derive(Debug, Clone, Serialize, Deserialize)]
4160pub struct MultiSelectEnumItems {
4161 #[serde(rename = "type")]
4162 pub schema_type: String,
4163 #[serde(rename = "enum")]
4164 pub enum_values: Vec<String>,
4165}
4166
4167#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4169#[serde(rename_all = "lowercase")]
4170#[non_exhaustive]
4171pub enum ElicitAction {
4172 Accept,
4174 Decline,
4176 Cancel,
4178}
4179
4180#[derive(Debug, Clone, Serialize, Deserialize)]
4182pub struct ElicitResult {
4183 pub action: ElicitAction,
4185 #[serde(default, skip_serializing_if = "Option::is_none")]
4187 pub content: Option<std::collections::HashMap<String, ElicitFieldValue>>,
4188 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4190 pub meta: Option<Value>,
4191}
4192
4193impl ElicitResult {
4194 pub fn accept(content: std::collections::HashMap<String, ElicitFieldValue>) -> Self {
4196 Self {
4197 action: ElicitAction::Accept,
4198 content: Some(content),
4199 meta: None,
4200 }
4201 }
4202
4203 pub fn decline() -> Self {
4205 Self {
4206 action: ElicitAction::Decline,
4207 content: None,
4208 meta: None,
4209 }
4210 }
4211
4212 pub fn cancel() -> Self {
4214 Self {
4215 action: ElicitAction::Cancel,
4216 content: None,
4217 meta: None,
4218 }
4219 }
4220}
4221
4222#[derive(Debug, Clone, Serialize, Deserialize)]
4224#[serde(untagged)]
4225#[non_exhaustive]
4226pub enum ElicitFieldValue {
4227 String(String),
4228 Number(f64),
4229 Integer(i64),
4230 Boolean(bool),
4231 StringArray(Vec<String>),
4232}
4233
4234#[derive(Debug, Clone, Serialize, Deserialize)]
4236#[serde(rename_all = "camelCase")]
4237pub struct ElicitationCompleteParams {
4238 pub elicitation_id: String,
4240 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4242 pub meta: Option<Value>,
4243}
4244
4245#[derive(Debug, Clone, Default, Serialize, Deserialize)]
4250pub struct EmptyResult {}
4251
4252impl McpRequest {
4257 pub fn from_jsonrpc(req: &JsonRpcRequest) -> Result<Self, crate::error::Error> {
4259 let params = req
4260 .params
4261 .clone()
4262 .unwrap_or(Value::Object(Default::default()));
4263
4264 match req.method.as_str() {
4265 "initialize" => {
4266 let p: InitializeParams = serde_json::from_value(params)?;
4267 Ok(McpRequest::Initialize(p))
4268 }
4269 "tools/list" => {
4270 let p: ListToolsParams = serde_json::from_value(params).unwrap_or_default();
4271 Ok(McpRequest::ListTools(p))
4272 }
4273 "tools/call" => {
4274 let p: CallToolParams = serde_json::from_value(params)?;
4275 Ok(McpRequest::CallTool(p))
4276 }
4277 "resources/list" => {
4278 let p: ListResourcesParams = serde_json::from_value(params).unwrap_or_default();
4279 Ok(McpRequest::ListResources(p))
4280 }
4281 "resources/templates/list" => {
4282 let p: ListResourceTemplatesParams =
4283 serde_json::from_value(params).unwrap_or_default();
4284 Ok(McpRequest::ListResourceTemplates(p))
4285 }
4286 "resources/read" => {
4287 let p: ReadResourceParams = serde_json::from_value(params)?;
4288 Ok(McpRequest::ReadResource(p))
4289 }
4290 "resources/subscribe" => {
4291 let p: SubscribeResourceParams = serde_json::from_value(params)?;
4292 Ok(McpRequest::SubscribeResource(p))
4293 }
4294 "resources/unsubscribe" => {
4295 let p: UnsubscribeResourceParams = serde_json::from_value(params)?;
4296 Ok(McpRequest::UnsubscribeResource(p))
4297 }
4298 "prompts/list" => {
4299 let p: ListPromptsParams = serde_json::from_value(params).unwrap_or_default();
4300 Ok(McpRequest::ListPrompts(p))
4301 }
4302 "prompts/get" => {
4303 let p: GetPromptParams = serde_json::from_value(params)?;
4304 Ok(McpRequest::GetPrompt(p))
4305 }
4306 "tasks/list" => {
4315 let p: ListTasksParams = serde_json::from_value(params).unwrap_or_default();
4316 Ok(McpRequest::ListTasks(p))
4317 }
4318 "tasks/get" => {
4319 let p: GetTaskInfoParams = serde_json::from_value(params)?;
4320 Ok(McpRequest::GetTaskInfo(p))
4321 }
4322 "tasks/update" => {
4323 let p: UpdateTaskParams = serde_json::from_value(params)?;
4324 Ok(McpRequest::UpdateTask(p))
4325 }
4326 "tasks/result" => {
4327 let p: GetTaskResultParams = serde_json::from_value(params)?;
4328 Ok(McpRequest::GetTaskResult(p))
4329 }
4330 "tasks/cancel" => {
4331 let p: CancelTaskParams = serde_json::from_value(params)?;
4332 Ok(McpRequest::CancelTask(p))
4333 }
4334 "ping" => Ok(McpRequest::Ping),
4335 "logging/setLevel" => {
4336 let p: SetLogLevelParams = serde_json::from_value(params)?;
4337 Ok(McpRequest::SetLoggingLevel(p))
4338 }
4339 "completion/complete" => {
4340 let p: CompleteParams = serde_json::from_value(params)?;
4341 Ok(McpRequest::Complete(p))
4342 }
4343 "server/discover" => {
4344 let p: DiscoverParams = serde_json::from_value(params).unwrap_or_default();
4346 Ok(McpRequest::Discover(p))
4347 }
4348 "messages/listen" => {
4349 let p: MessagesListenParams = serde_json::from_value(params).unwrap_or_default();
4352 Ok(McpRequest::MessagesListen(p))
4353 }
4354 method => Ok(McpRequest::Unknown {
4355 method: method.to_string(),
4356 params: req.params.clone(),
4357 }),
4358 }
4359 }
4360}
4361
4362impl McpNotification {
4363 pub fn from_jsonrpc(notif: &JsonRpcNotification) -> Result<Self, crate::error::Error> {
4365 let params = notif
4366 .params
4367 .clone()
4368 .unwrap_or(Value::Object(Default::default()));
4369
4370 match notif.method.as_str() {
4371 notifications::INITIALIZED => Ok(McpNotification::Initialized),
4372 notifications::CANCELLED => {
4373 let p: CancelledParams = serde_json::from_value(params)?;
4374 Ok(McpNotification::Cancelled(p))
4375 }
4376 notifications::PROGRESS => {
4377 let p: ProgressParams = serde_json::from_value(params)?;
4378 Ok(McpNotification::Progress(p))
4379 }
4380 notifications::ROOTS_LIST_CHANGED => Ok(McpNotification::RootsListChanged),
4381 method => Ok(McpNotification::Unknown {
4382 method: method.to_string(),
4383 params: notif.params.clone(),
4384 }),
4385 }
4386 }
4387}
4388
4389#[cfg(test)]
4390mod tests {
4391 use super::*;
4392 use crate::error::JsonRpcError;
4393
4394 #[test]
4399 fn upcoming_version_is_documented_and_not_yet_supported() {
4400 assert_eq!(UPCOMING_PROTOCOL_VERSION, "2026-07-28");
4405 assert!(
4406 !SUPPORTED_PROTOCOL_VERSIONS.contains(&UPCOMING_PROTOCOL_VERSION),
4407 "do not promote UPCOMING_PROTOCOL_VERSION into SUPPORTED until the \
4408 impl is complete; current: {:?}",
4409 SUPPORTED_PROTOCOL_VERSIONS
4410 );
4411 assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&LATEST_PROTOCOL_VERSION));
4414 }
4415
4416 #[test]
4421 fn discover_request_round_trips_with_no_params() {
4422 let no_params = r#"{"jsonrpc":"2.0","id":1,"method":"server/discover"}"#;
4425 let req: JsonRpcRequest = serde_json::from_str(no_params).unwrap();
4426 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
4427 assert!(matches!(parsed, McpRequest::Discover(_)));
4428 assert_eq!(parsed.method_name(), "server/discover");
4429
4430 let empty_params = r#"{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{}}"#;
4431 let req: JsonRpcRequest = serde_json::from_str(empty_params).unwrap();
4432 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
4433 assert!(matches!(parsed, McpRequest::Discover(_)));
4434 }
4435
4436 #[test]
4437 fn discover_result_serializes_supported_versions_array() {
4438 let r = DiscoverResult {
4439 supported_versions: vec!["2026-07-28".into(), "2025-11-25".into()],
4440 capabilities: ServerCapabilities::default(),
4441 server_info: Implementation {
4442 name: "test-server".into(),
4443 version: "1.0.0".into(),
4444 title: None,
4445 description: None,
4446 icons: None,
4447 website_url: None,
4448 meta: None,
4449 },
4450 instructions: None,
4451 meta: None,
4452 };
4453 let json = serde_json::to_value(&r).unwrap();
4454 assert_eq!(
4455 json["supportedVersions"],
4456 serde_json::json!(["2026-07-28", "2025-11-25"])
4457 );
4458 assert!(
4459 json.get("protocolVersion").is_none(),
4460 "server/discover must use supportedVersions, not protocolVersion, got: {json}"
4461 );
4462 assert_eq!(json["serverInfo"]["name"], "test-server");
4463 assert!(
4464 json.get("instructions").is_none(),
4465 "instructions must be omitted when None, got: {json}"
4466 );
4467 }
4468
4469 #[test]
4474 fn messages_listen_request_round_trips_with_no_params() {
4475 let no_params = r#"{"jsonrpc":"2.0","id":1,"method":"messages/listen"}"#;
4477 let req: JsonRpcRequest = serde_json::from_str(no_params).unwrap();
4478 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
4479 assert!(matches!(parsed, McpRequest::MessagesListen(_)));
4480 assert_eq!(parsed.method_name(), "messages/listen");
4481
4482 let empty_params = r#"{"jsonrpc":"2.0","id":2,"method":"messages/listen","params":{}}"#;
4483 let req: JsonRpcRequest = serde_json::from_str(empty_params).unwrap();
4484 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
4485 assert!(matches!(parsed, McpRequest::MessagesListen(_)));
4486 }
4487
4488 #[test]
4489 fn messages_listen_request_serializes_with_correct_method() {
4490 let req = JsonRpcRequest::new(42i64, "messages/listen");
4491 let json = serde_json::to_string(&req).unwrap();
4492 let v: serde_json::Value = serde_json::from_str(&json).unwrap();
4493 assert_eq!(v["method"], "messages/listen");
4494 assert_eq!(v["jsonrpc"], "2.0");
4495 assert_eq!(v["id"], 42);
4496 }
4497
4498 #[test]
4503 fn list_tools_result_omits_ttl_and_cache_scope_by_default() {
4504 let r = ListToolsResult {
4505 tools: vec![],
4506 next_cursor: None,
4507 ttl_ms: None,
4508 cache_scope: None,
4509 meta: None,
4510 };
4511 let json = serde_json::to_value(&r).unwrap();
4512 assert!(
4513 json.get("ttlMs").is_none(),
4514 "ttlMs must be omitted when None, got: {json}"
4515 );
4516 assert!(
4517 json.get("cacheScope").is_none(),
4518 "cacheScope must be omitted when None, got: {json}"
4519 );
4520 }
4521
4522 #[test]
4523 fn list_tools_result_emits_ttl_and_cache_scope_when_set() {
4524 let r = ListToolsResult {
4525 tools: vec![],
4526 next_cursor: None,
4527 ttl_ms: Some(60_000),
4528 cache_scope: Some(CacheScope::Global),
4529 meta: None,
4530 };
4531 let json = serde_json::to_value(&r).unwrap();
4532 assert_eq!(json["ttlMs"], 60_000);
4533 assert_eq!(json["cacheScope"], "global");
4534 }
4535
4536 #[test]
4537 fn cache_scope_roundtrips_via_lowercase_strings() {
4538 for (scope, wire) in [
4539 (CacheScope::Session, "session"),
4540 (CacheScope::Global, "global"),
4541 ] {
4542 let s = serde_json::to_value(scope).unwrap();
4543 assert_eq!(s, serde_json::json!(wire));
4544 let back: CacheScope = serde_json::from_value(s).unwrap();
4545 assert_eq!(back, scope);
4546 }
4547 }
4548
4549 #[test]
4554 fn roots_capability_omits_deprecated_by_default() {
4555 let cap = RootsCapability {
4556 list_changed: true,
4557 deprecated: None,
4558 };
4559 let json = serde_json::to_value(&cap).unwrap();
4560 assert!(
4561 json.get("deprecated").is_none(),
4562 "deprecated must be omitted when None to avoid changing wire output for existing servers, got: {json}"
4563 );
4564 }
4565
4566 #[test]
4567 fn capability_emits_deprecation_info_when_flagged() {
4568 let cap = LoggingCapability {
4569 deprecated: Some(DeprecationInfo {
4570 since: Some("2026-07-28".into()),
4571 remove_in: Some("2027-07-28".into()),
4572 message: Some("Logging moves to OpenTelemetry per SEP-2577".into()),
4573 replacement: Some(
4574 "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2577"
4575 .into(),
4576 ),
4577 }),
4578 };
4579 let json = serde_json::to_value(&cap).unwrap();
4580 let dep = &json["deprecated"];
4581 assert_eq!(dep["since"], "2026-07-28");
4582 assert_eq!(dep["removeIn"], "2027-07-28");
4583 assert!(dep["message"].as_str().unwrap().contains("OpenTelemetry"));
4584 assert!(dep["replacement"].as_str().unwrap().contains("2577"));
4585 }
4586
4587 #[test]
4588 fn deprecation_info_round_trip_with_partial_fields() {
4589 let wire = r#"{"since":"2026-07-28"}"#;
4592 let info: DeprecationInfo = serde_json::from_str(wire).unwrap();
4593 assert_eq!(info.since.as_deref(), Some("2026-07-28"));
4594 assert!(info.remove_in.is_none());
4595 let back = serde_json::to_string(&info).unwrap();
4596 assert_eq!(back, wire);
4597 }
4598
4599 #[test]
4600 fn cancelled_params_serializes_request_id_when_present() {
4601 let p = CancelledParams {
4602 request_id: Some(RequestId::Number(42)),
4603 reason: Some("user abort".into()),
4604 meta: None,
4605 };
4606 let json = serde_json::to_value(&p).unwrap();
4607 assert_eq!(json["requestId"], serde_json::json!(42));
4608 assert_eq!(json["reason"], serde_json::json!("user abort"));
4609 }
4610
4611 #[test]
4612 fn cancelled_params_emits_null_request_id_when_absent() {
4613 let p = CancelledParams {
4618 request_id: None,
4619 reason: None,
4620 meta: None,
4621 };
4622 let json = serde_json::to_value(&p).unwrap();
4623 assert!(
4624 json.get("requestId").is_some(),
4625 "requestId field must be present per MCP spec, got: {json}"
4626 );
4627 assert!(
4628 json["requestId"].is_null(),
4629 "requestId must serialize as null when unset, got: {}",
4630 json["requestId"]
4631 );
4632 }
4633
4634 #[test]
4635 fn cancelled_params_deserializes_null_request_id() {
4636 let wire = r#"{"requestId":null,"reason":"x"}"#;
4637 let p: CancelledParams = serde_json::from_str(wire).unwrap();
4638 assert!(p.request_id.is_none());
4639 assert_eq!(p.reason.as_deref(), Some("x"));
4640 }
4641
4642 #[test]
4643 fn cancelled_params_deserializes_missing_request_id() {
4644 let wire = r#"{"reason":"x"}"#;
4646 let p: CancelledParams = serde_json::from_str(wire).unwrap();
4647 assert!(p.request_id.is_none());
4648 }
4649
4650 #[test]
4651 fn error_response_serializes_id_null_when_unknown() {
4652 let resp = JsonRpcResponse::error(None, JsonRpcError::parse_error("bad json"));
4653 let json = serde_json::to_value(&resp).unwrap();
4654 assert!(
4655 json.get("id").is_some(),
4656 "id field must be present per JSON-RPC 2.0, got: {json}"
4657 );
4658 assert!(
4659 json["id"].is_null(),
4660 "id must serialize as null when unknown, got: {}",
4661 json["id"]
4662 );
4663 }
4664
4665 #[test]
4666 fn error_response_serializes_id_when_known() {
4667 let resp =
4668 JsonRpcResponse::error(Some(RequestId::Number(7)), JsonRpcError::parse_error("bad"));
4669 let json = serde_json::to_value(&resp).unwrap();
4670 assert_eq!(json["id"], serde_json::json!(7));
4671 }
4672
4673 #[test]
4674 fn error_response_deserializes_null_id() {
4675 let wire = r#"{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"x"}}"#;
4676 let resp: JsonRpcResponse = serde_json::from_str(wire).unwrap();
4677 match resp {
4678 JsonRpcResponse::Error(e) => assert!(e.id.is_none()),
4679 _ => panic!("expected Error variant"),
4680 }
4681 }
4682
4683 #[test]
4684 fn test_content_text_constructor() {
4685 let content = Content::text("hello world");
4686 assert_eq!(content.as_text(), Some("hello world"));
4687
4688 match &content {
4690 Content::Text {
4691 text, annotations, ..
4692 } => {
4693 assert_eq!(text, "hello world");
4694 assert!(annotations.is_none());
4695 }
4696 _ => panic!("expected Content::Text"),
4697 }
4698
4699 let content = Content::text(String::from("owned"));
4701 assert_eq!(content.as_text(), Some("owned"));
4702 }
4703
4704 #[test]
4705 fn test_elicit_form_schema_builder() {
4706 let schema = ElicitFormSchema::new()
4707 .string_field("name", Some("Your name"), true)
4708 .number_field("age", Some("Your age"), false)
4709 .boolean_field("agree", Some("Do you agree?"), true)
4710 .enum_field(
4711 "color",
4712 Some("Favorite color"),
4713 vec!["red".to_string(), "green".to_string(), "blue".to_string()],
4714 false,
4715 );
4716
4717 assert_eq!(schema.schema_type, "object");
4718 assert_eq!(schema.properties.len(), 4);
4719 assert_eq!(schema.required.len(), 2);
4720 assert!(schema.required.contains(&"name".to_string()));
4721 assert!(schema.required.contains(&"agree".to_string()));
4722 }
4723
4724 #[test]
4725 fn test_elicit_form_schema_serialization() {
4726 let schema = ElicitFormSchema::new().string_field("username", Some("Enter username"), true);
4727
4728 let json = serde_json::to_value(&schema).unwrap();
4729 assert_eq!(json["type"], "object");
4730 assert!(json["properties"]["username"]["type"] == "string");
4731 assert!(
4732 json["required"]
4733 .as_array()
4734 .unwrap()
4735 .contains(&serde_json::json!("username"))
4736 );
4737 }
4738
4739 #[test]
4740 fn test_elicit_result_accept() {
4741 let mut content = std::collections::HashMap::new();
4742 content.insert(
4743 "name".to_string(),
4744 ElicitFieldValue::String("Alice".to_string()),
4745 );
4746 content.insert("age".to_string(), ElicitFieldValue::Integer(30));
4747
4748 let result = ElicitResult::accept(content);
4749 assert_eq!(result.action, ElicitAction::Accept);
4750 assert!(result.content.is_some());
4751 }
4752
4753 #[test]
4754 fn test_elicit_result_decline() {
4755 let result = ElicitResult::decline();
4756 assert_eq!(result.action, ElicitAction::Decline);
4757 assert!(result.content.is_none());
4758 }
4759
4760 #[test]
4761 fn test_elicit_result_cancel() {
4762 let result = ElicitResult::cancel();
4763 assert_eq!(result.action, ElicitAction::Cancel);
4764 assert!(result.content.is_none());
4765 }
4766
4767 #[test]
4768 fn test_elicit_mode_serialization() {
4769 assert_eq!(
4770 serde_json::to_string(&ElicitMode::Form).unwrap(),
4771 "\"form\""
4772 );
4773 assert_eq!(serde_json::to_string(&ElicitMode::Url).unwrap(), "\"url\"");
4774 }
4775
4776 #[test]
4777 fn test_elicit_action_serialization() {
4778 assert_eq!(
4779 serde_json::to_string(&ElicitAction::Accept).unwrap(),
4780 "\"accept\""
4781 );
4782 assert_eq!(
4783 serde_json::to_string(&ElicitAction::Decline).unwrap(),
4784 "\"decline\""
4785 );
4786 assert_eq!(
4787 serde_json::to_string(&ElicitAction::Cancel).unwrap(),
4788 "\"cancel\""
4789 );
4790 }
4791
4792 #[test]
4793 fn test_elicitation_capability() {
4794 let cap = ElicitationCapability {
4795 form: Some(ElicitationFormCapability {}),
4796 url: None,
4797 };
4798
4799 let json = serde_json::to_value(&cap).unwrap();
4800 assert!(json["form"].is_object());
4801 assert!(json.get("url").is_none());
4802 }
4803
4804 #[test]
4805 fn test_client_capabilities_with_elicitation() {
4806 let caps = ClientCapabilities {
4807 roots: None,
4808 sampling: None,
4809 elicitation: Some(ElicitationCapability {
4810 form: Some(ElicitationFormCapability {}),
4811 url: Some(ElicitationUrlCapability {}),
4812 }),
4813 tasks: None,
4814 experimental: None,
4815 extensions: None,
4816 };
4817
4818 let json = serde_json::to_value(&caps).unwrap();
4819 assert!(json["elicitation"]["form"].is_object());
4820 assert!(json["elicitation"]["url"].is_object());
4821 }
4822
4823 #[test]
4824 fn test_elicit_url_params() {
4825 let params = ElicitUrlParams {
4826 mode: Some(ElicitMode::Url),
4827 elicitation_id: "abc123".to_string(),
4828 message: "Please authorize".to_string(),
4829 url: "https://example.com/auth".to_string(),
4830 meta: None,
4831 };
4832
4833 let json = serde_json::to_value(¶ms).unwrap();
4834 assert_eq!(json["mode"], "url");
4835 assert_eq!(json["elicitationId"], "abc123");
4836 assert_eq!(json["message"], "Please authorize");
4837 assert_eq!(json["url"], "https://example.com/auth");
4838 }
4839
4840 #[test]
4841 fn test_elicitation_complete_params() {
4842 let params = ElicitationCompleteParams {
4843 elicitation_id: "xyz789".to_string(),
4844 meta: None,
4845 };
4846
4847 let json = serde_json::to_value(¶ms).unwrap();
4848 assert_eq!(json["elicitationId"], "xyz789");
4849 }
4850
4851 #[test]
4852 fn test_root_new() {
4853 let root = Root::new("file:///home/user/project");
4854 assert_eq!(root.uri, "file:///home/user/project");
4855 assert!(root.name.is_none());
4856 }
4857
4858 #[test]
4859 fn test_root_with_name() {
4860 let root = Root::with_name("file:///home/user/project", "My Project");
4861 assert_eq!(root.uri, "file:///home/user/project");
4862 assert_eq!(root.name.as_deref(), Some("My Project"));
4863 }
4864
4865 #[test]
4866 fn test_root_serialization() {
4867 let root = Root::with_name("file:///workspace", "Workspace");
4868 let json = serde_json::to_value(&root).unwrap();
4869 assert_eq!(json["uri"], "file:///workspace");
4870 assert_eq!(json["name"], "Workspace");
4871 }
4872
4873 #[test]
4874 fn test_root_serialization_without_name() {
4875 let root = Root::new("file:///workspace");
4876 let json = serde_json::to_value(&root).unwrap();
4877 assert_eq!(json["uri"], "file:///workspace");
4878 assert!(json.get("name").is_none());
4879 }
4880
4881 #[test]
4882 fn test_root_deserialization() {
4883 let json = serde_json::json!({
4884 "uri": "file:///home/user",
4885 "name": "Home"
4886 });
4887 let root: Root = serde_json::from_value(json).unwrap();
4888 assert_eq!(root.uri, "file:///home/user");
4889 assert_eq!(root.name.as_deref(), Some("Home"));
4890 }
4891
4892 #[test]
4893 fn test_list_roots_result() {
4894 let result = ListRootsResult {
4895 roots: vec![
4896 Root::new("file:///project1"),
4897 Root::with_name("file:///project2", "Project 2"),
4898 ],
4899 meta: None,
4900 };
4901
4902 let json = serde_json::to_value(&result).unwrap();
4903 let roots = json["roots"].as_array().unwrap();
4904 assert_eq!(roots.len(), 2);
4905 assert_eq!(roots[0]["uri"], "file:///project1");
4906 assert_eq!(roots[1]["name"], "Project 2");
4907 }
4908
4909 #[test]
4910 fn test_roots_capability_serialization() {
4911 let cap = RootsCapability {
4912 list_changed: true,
4913 deprecated: None,
4914 };
4915 let json = serde_json::to_value(&cap).unwrap();
4916 assert_eq!(json["listChanged"], true);
4917 }
4918
4919 #[test]
4920 fn test_client_capabilities_with_roots() {
4921 let caps = ClientCapabilities {
4922 roots: Some(RootsCapability {
4923 list_changed: true,
4924 deprecated: None,
4925 }),
4926 sampling: None,
4927 elicitation: None,
4928 tasks: None,
4929 experimental: None,
4930 extensions: None,
4931 };
4932
4933 let json = serde_json::to_value(&caps).unwrap();
4934 assert_eq!(json["roots"]["listChanged"], true);
4935 }
4936
4937 #[test]
4938 fn test_roots_list_changed_notification_parsing() {
4939 let notif = JsonRpcNotification {
4940 jsonrpc: "2.0".to_string(),
4941 method: notifications::ROOTS_LIST_CHANGED.to_string(),
4942 params: None,
4943 };
4944
4945 let mcp_notif = McpNotification::from_jsonrpc(¬if).unwrap();
4946 assert!(matches!(mcp_notif, McpNotification::RootsListChanged));
4947 }
4948
4949 #[test]
4954 fn test_prompt_reference() {
4955 let ref_ = PromptReference::new("my-prompt");
4956 assert_eq!(ref_.ref_type, "ref/prompt");
4957 assert_eq!(ref_.name, "my-prompt");
4958
4959 let json = serde_json::to_value(&ref_).unwrap();
4960 assert_eq!(json["type"], "ref/prompt");
4961 assert_eq!(json["name"], "my-prompt");
4962 }
4963
4964 #[test]
4965 fn test_resource_reference() {
4966 let ref_ = ResourceReference::new("file:///path/to/file");
4967 assert_eq!(ref_.ref_type, "ref/resource");
4968 assert_eq!(ref_.uri, "file:///path/to/file");
4969
4970 let json = serde_json::to_value(&ref_).unwrap();
4971 assert_eq!(json["type"], "ref/resource");
4972 assert_eq!(json["uri"], "file:///path/to/file");
4973 }
4974
4975 #[test]
4976 fn test_completion_reference_prompt() {
4977 let ref_ = CompletionReference::prompt("test-prompt");
4978 let json = serde_json::to_value(&ref_).unwrap();
4979 assert_eq!(json["type"], "ref/prompt");
4980 assert_eq!(json["name"], "test-prompt");
4981 }
4982
4983 #[test]
4984 fn test_completion_reference_resource() {
4985 let ref_ = CompletionReference::resource("file:///test");
4986 let json = serde_json::to_value(&ref_).unwrap();
4987 assert_eq!(json["type"], "ref/resource");
4988 assert_eq!(json["uri"], "file:///test");
4989 }
4990
4991 #[test]
4992 fn test_completion_argument() {
4993 let arg = CompletionArgument::new("query", "SELECT * FROM");
4994 assert_eq!(arg.name, "query");
4995 assert_eq!(arg.value, "SELECT * FROM");
4996 }
4997
4998 #[test]
4999 fn test_complete_params_serialization() {
5000 let params = CompleteParams {
5001 reference: CompletionReference::prompt("sql-prompt"),
5002 argument: CompletionArgument::new("query", "SEL"),
5003 context: None,
5004 meta: None,
5005 };
5006
5007 let json = serde_json::to_value(¶ms).unwrap();
5008 assert_eq!(json["ref"]["type"], "ref/prompt");
5009 assert_eq!(json["ref"]["name"], "sql-prompt");
5010 assert_eq!(json["argument"]["name"], "query");
5011 assert_eq!(json["argument"]["value"], "SEL");
5012 assert!(json.get("context").is_none()); }
5014
5015 #[test]
5016 fn test_completion_new() {
5017 let completion = Completion::new(vec!["SELECT".to_string(), "SET".to_string()]);
5018 assert_eq!(completion.values.len(), 2);
5019 assert!(completion.total.is_none());
5020 assert!(completion.has_more.is_none());
5021 }
5022
5023 #[test]
5024 fn test_completion_with_pagination() {
5025 let completion =
5026 Completion::with_pagination(vec!["a".to_string(), "b".to_string()], 100, true);
5027 assert_eq!(completion.values.len(), 2);
5028 assert_eq!(completion.total, Some(100));
5029 assert_eq!(completion.has_more, Some(true));
5030 }
5031
5032 #[test]
5033 fn test_complete_result() {
5034 let result = CompleteResult::new(vec!["option1".to_string(), "option2".to_string()]);
5035 let json = serde_json::to_value(&result).unwrap();
5036 assert!(json["completion"]["values"].is_array());
5037 assert_eq!(json["completion"]["values"][0], "option1");
5038 }
5039
5040 #[test]
5045 fn test_model_hint() {
5046 let hint = ModelHint::new("claude-3-opus");
5047 assert_eq!(hint.name, Some("claude-3-opus".to_string()));
5048 }
5049
5050 #[test]
5051 fn test_model_preferences_builder() {
5052 let prefs = ModelPreferences::new()
5053 .speed(0.8)
5054 .intelligence(0.9)
5055 .cost(0.5)
5056 .hint("gpt-4")
5057 .hint("claude-3");
5058
5059 assert_eq!(prefs.speed_priority, Some(0.8));
5060 assert_eq!(prefs.intelligence_priority, Some(0.9));
5061 assert_eq!(prefs.cost_priority, Some(0.5));
5062 assert_eq!(prefs.hints.len(), 2);
5063 }
5064
5065 #[test]
5066 fn test_model_preferences_clamping() {
5067 let prefs = ModelPreferences::new().speed(1.5).cost(-0.5);
5068
5069 assert_eq!(prefs.speed_priority, Some(1.0)); assert_eq!(prefs.cost_priority, Some(0.0)); }
5072
5073 #[test]
5074 fn test_include_context_serialization() {
5075 assert_eq!(
5076 serde_json::to_string(&IncludeContext::AllServers).unwrap(),
5077 "\"allServers\""
5078 );
5079 assert_eq!(
5080 serde_json::to_string(&IncludeContext::ThisServer).unwrap(),
5081 "\"thisServer\""
5082 );
5083 assert_eq!(
5084 serde_json::to_string(&IncludeContext::None).unwrap(),
5085 "\"none\""
5086 );
5087 }
5088
5089 #[test]
5090 fn test_sampling_message_user() {
5091 let msg = SamplingMessage::user("Hello, how are you?");
5092 assert_eq!(msg.role, ContentRole::User);
5093 assert!(
5094 matches!(msg.content, SamplingContentOrArray::Single(SamplingContent::Text { ref text, .. }) if text == "Hello, how are you?")
5095 );
5096 }
5097
5098 #[test]
5099 fn test_sampling_message_assistant() {
5100 let msg = SamplingMessage::assistant("I'm doing well!");
5101 assert_eq!(msg.role, ContentRole::Assistant);
5102 }
5103
5104 #[test]
5105 fn test_sampling_content_text_serialization() {
5106 let content = SamplingContent::Text {
5107 text: "Hello".to_string(),
5108 annotations: None,
5109 meta: None,
5110 };
5111 let json = serde_json::to_value(&content).unwrap();
5112 assert_eq!(json["type"], "text");
5113 assert_eq!(json["text"], "Hello");
5114 }
5115
5116 #[test]
5117 fn test_sampling_content_image_serialization() {
5118 let content = SamplingContent::Image {
5119 data: "base64data".to_string(),
5120 mime_type: "image/png".to_string(),
5121 annotations: None,
5122 meta: None,
5123 };
5124 let json = serde_json::to_value(&content).unwrap();
5125 assert_eq!(json["type"], "image");
5126 assert_eq!(json["data"], "base64data");
5127 assert_eq!(json["mimeType"], "image/png");
5128 }
5129
5130 #[test]
5131 fn test_create_message_params() {
5132 let params = CreateMessageParams::new(
5133 vec![
5134 SamplingMessage::user("What is 2+2?"),
5135 SamplingMessage::assistant("4"),
5136 SamplingMessage::user("And 3+3?"),
5137 ],
5138 100,
5139 )
5140 .system_prompt("You are a math tutor")
5141 .temperature(0.7)
5142 .stop_sequence("END")
5143 .include_context(IncludeContext::ThisServer);
5144
5145 assert_eq!(params.messages.len(), 3);
5146 assert_eq!(params.max_tokens, 100);
5147 assert_eq!(
5148 params.system_prompt.as_deref(),
5149 Some("You are a math tutor")
5150 );
5151 assert_eq!(params.temperature, Some(0.7));
5152 assert_eq!(params.stop_sequences.len(), 1);
5153 assert_eq!(params.include_context, Some(IncludeContext::ThisServer));
5154 }
5155
5156 #[test]
5157 fn test_create_message_params_serialization() {
5158 let params = CreateMessageParams::new(vec![SamplingMessage::user("Hello")], 50);
5159
5160 let json = serde_json::to_value(¶ms).unwrap();
5161 assert!(json["messages"].is_array());
5162 assert_eq!(json["maxTokens"], 50);
5163 }
5164
5165 #[test]
5166 fn test_create_message_result_deserialization() {
5167 let json = serde_json::json!({
5168 "content": {
5169 "type": "text",
5170 "text": "The answer is 42"
5171 },
5172 "model": "claude-3-opus",
5173 "role": "assistant",
5174 "stopReason": "end_turn"
5175 });
5176
5177 let result: CreateMessageResult = serde_json::from_value(json).unwrap();
5178 assert_eq!(result.model, "claude-3-opus");
5179 assert_eq!(result.role, ContentRole::Assistant);
5180 assert_eq!(result.stop_reason.as_deref(), Some("end_turn"));
5181 }
5182
5183 #[test]
5184 fn test_completions_capability_serialization() {
5185 let cap = CompletionsCapability {};
5186 let json = serde_json::to_value(&cap).unwrap();
5187 assert!(json.is_object());
5188 }
5189
5190 #[test]
5191 fn test_server_capabilities_with_completions() {
5192 let caps = ServerCapabilities {
5193 completions: Some(CompletionsCapability {}),
5194 ..Default::default()
5195 };
5196
5197 let json = serde_json::to_value(&caps).unwrap();
5198 assert!(json["completions"].is_object());
5199 }
5200
5201 #[test]
5202 fn test_content_resource_link_serialization() {
5203 let content = Content::ResourceLink {
5204 uri: "file:///test.txt".to_string(),
5205 name: "test.txt".to_string(),
5206 title: None,
5207 description: Some("A test file".to_string()),
5208 mime_type: Some("text/plain".to_string()),
5209 size: None,
5210 icons: None,
5211 annotations: None,
5212 meta: None,
5213 };
5214 let json = serde_json::to_value(&content).unwrap();
5215 assert_eq!(json["type"], "resource_link");
5216 assert_eq!(json["uri"], "file:///test.txt");
5217 assert_eq!(json["name"], "test.txt");
5218 assert_eq!(json["description"], "A test file");
5219 assert_eq!(json["mimeType"], "text/plain");
5220 }
5221
5222 #[test]
5223 fn test_call_tool_result_resource_link() {
5224 let result = CallToolResult::resource_link("file:///output.json", "output.json");
5225 assert_eq!(result.content.len(), 1);
5226 assert!(!result.is_error);
5227 match &result.content[0] {
5228 Content::ResourceLink { uri, .. } => assert_eq!(uri, "file:///output.json"),
5229 _ => panic!("Expected ResourceLink content"),
5230 }
5231 }
5232
5233 #[test]
5234 fn test_call_tool_result_image() {
5235 let result = CallToolResult::image("base64data", "image/png");
5236 assert_eq!(result.content.len(), 1);
5237 match &result.content[0] {
5238 Content::Image {
5239 data, mime_type, ..
5240 } => {
5241 assert_eq!(data, "base64data");
5242 assert_eq!(mime_type, "image/png");
5243 }
5244 _ => panic!("Expected Image content"),
5245 }
5246 }
5247
5248 #[test]
5249 fn test_call_tool_result_audio() {
5250 let result = CallToolResult::audio("audiodata", "audio/wav");
5251 assert_eq!(result.content.len(), 1);
5252 match &result.content[0] {
5253 Content::Audio {
5254 data, mime_type, ..
5255 } => {
5256 assert_eq!(data, "audiodata");
5257 assert_eq!(mime_type, "audio/wav");
5258 }
5259 _ => panic!("Expected Audio content"),
5260 }
5261 }
5262
5263 #[test]
5264 fn test_sampling_tool_serialization() {
5265 let tool = SamplingTool {
5266 name: "get_weather".to_string(),
5267 title: None,
5268 description: Some("Get current weather".to_string()),
5269 input_schema: serde_json::json!({
5270 "type": "object",
5271 "properties": {
5272 "location": { "type": "string" }
5273 }
5274 }),
5275 output_schema: None,
5276 icons: None,
5277 annotations: None,
5278 execution: None,
5279 };
5280 let json = serde_json::to_value(&tool).unwrap();
5281 assert_eq!(json["name"], "get_weather");
5282 assert_eq!(json["description"], "Get current weather");
5283 assert!(json["inputSchema"]["properties"]["location"].is_object());
5284 }
5285
5286 #[test]
5287 fn test_tool_choice_modes() {
5288 let auto = ToolChoice::auto();
5289 assert_eq!(auto.mode, "auto");
5290 assert!(auto.name.is_none());
5291
5292 let required = ToolChoice::required();
5293 assert_eq!(required.mode, "required");
5294
5295 let none = ToolChoice::none();
5296 assert_eq!(none.mode, "none");
5297
5298 let tool = ToolChoice::tool("get_weather");
5299 assert_eq!(tool.mode, "tool");
5300 assert_eq!(tool.name.as_deref(), Some("get_weather"));
5301
5302 let json = serde_json::to_value(&auto).unwrap();
5304 assert_eq!(json["mode"], "auto");
5305 assert!(json.get("name").is_none());
5306
5307 let json = serde_json::to_value(&tool).unwrap();
5308 assert_eq!(json["mode"], "tool");
5309 assert_eq!(json["name"], "get_weather");
5310 }
5311
5312 #[test]
5313 fn test_sampling_content_tool_use() {
5314 let content = SamplingContent::ToolUse {
5315 id: "tool_123".to_string(),
5316 name: "get_weather".to_string(),
5317 input: serde_json::json!({"location": "San Francisco"}),
5318 meta: None,
5319 };
5320 let json = serde_json::to_value(&content).unwrap();
5321 assert_eq!(json["type"], "tool_use");
5322 assert_eq!(json["id"], "tool_123");
5323 assert_eq!(json["name"], "get_weather");
5324 assert_eq!(json["input"]["location"], "San Francisco");
5325 }
5326
5327 #[test]
5328 fn test_sampling_content_tool_result() {
5329 let content = SamplingContent::ToolResult {
5330 tool_use_id: "tool_123".to_string(),
5331 content: vec![SamplingContent::Text {
5332 text: "72F, sunny".to_string(),
5333 annotations: None,
5334 meta: None,
5335 }],
5336 structured_content: None,
5337 is_error: None,
5338 meta: None,
5339 };
5340 let json = serde_json::to_value(&content).unwrap();
5341 assert_eq!(json["type"], "tool_result");
5342 assert_eq!(json["toolUseId"], "tool_123");
5343 assert_eq!(json["content"][0]["type"], "text");
5344 }
5345
5346 #[test]
5347 fn test_sampling_content_or_array_single() {
5348 let json = serde_json::json!({
5349 "type": "text",
5350 "text": "Hello"
5351 });
5352 let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
5353 let items = content.items();
5354 assert_eq!(items.len(), 1);
5355 match items[0] {
5356 SamplingContent::Text { text, .. } => assert_eq!(text, "Hello"),
5357 _ => panic!("Expected text content"),
5358 }
5359 }
5360
5361 #[test]
5362 fn test_sampling_content_or_array_multiple() {
5363 let json = serde_json::json!([
5364 { "type": "text", "text": "Hello" },
5365 { "type": "text", "text": "World" }
5366 ]);
5367 let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
5368 let items = content.items();
5369 assert_eq!(items.len(), 2);
5370 }
5371
5372 #[test]
5373 fn test_create_message_params_with_tools() {
5374 let tool = SamplingTool {
5375 name: "calculator".to_string(),
5376 title: None,
5377 description: Some("Do math".to_string()),
5378 input_schema: serde_json::json!({"type": "object"}),
5379 output_schema: None,
5380 icons: None,
5381 annotations: None,
5382 execution: None,
5383 };
5384 let params = CreateMessageParams::new(vec![], 100)
5385 .tools(vec![tool])
5386 .tool_choice(ToolChoice::auto());
5387
5388 let json = serde_json::to_value(¶ms).unwrap();
5389 assert!(json["tools"].is_array());
5390 assert_eq!(json["tools"][0]["name"], "calculator");
5391 assert_eq!(json["toolChoice"]["mode"], "auto");
5392 }
5393
5394 #[test]
5395 fn test_create_message_result_content_items() {
5396 let result = CreateMessageResult {
5397 content: SamplingContentOrArray::Array(vec![
5398 SamplingContent::Text {
5399 text: "First".to_string(),
5400 annotations: None,
5401 meta: None,
5402 },
5403 SamplingContent::Text {
5404 text: "Second".to_string(),
5405 annotations: None,
5406 meta: None,
5407 },
5408 ]),
5409 model: "test".to_string(),
5410 role: ContentRole::Assistant,
5411 stop_reason: None,
5412 meta: None,
5413 };
5414 let items = result.content_items();
5415 assert_eq!(items.len(), 2);
5416 }
5417
5418 #[test]
5419 fn test_sampling_content_as_text() {
5420 let text_content = SamplingContent::Text {
5421 text: "Hello".to_string(),
5422 annotations: None,
5423 meta: None,
5424 };
5425 assert_eq!(text_content.as_text(), Some("Hello"));
5426
5427 let image_content = SamplingContent::Image {
5428 data: "base64data".to_string(),
5429 mime_type: "image/png".to_string(),
5430 annotations: None,
5431 meta: None,
5432 };
5433 assert_eq!(image_content.as_text(), None);
5434
5435 let audio_content = SamplingContent::Audio {
5436 data: "base64audio".to_string(),
5437 mime_type: "audio/wav".to_string(),
5438 annotations: None,
5439 meta: None,
5440 };
5441 assert_eq!(audio_content.as_text(), None);
5442 }
5443
5444 #[test]
5445 fn test_create_message_result_first_text_single() {
5446 let result = CreateMessageResult {
5447 content: SamplingContentOrArray::Single(SamplingContent::Text {
5448 text: "Hello, world!".to_string(),
5449 annotations: None,
5450 meta: None,
5451 }),
5452 model: "test".to_string(),
5453 role: ContentRole::Assistant,
5454 stop_reason: None,
5455 meta: None,
5456 };
5457 assert_eq!(result.first_text(), Some("Hello, world!"));
5458 }
5459
5460 #[test]
5461 fn test_create_message_result_first_text_array() {
5462 let result = CreateMessageResult {
5463 content: SamplingContentOrArray::Array(vec![
5464 SamplingContent::Text {
5465 text: "First".to_string(),
5466 annotations: None,
5467 meta: None,
5468 },
5469 SamplingContent::Text {
5470 text: "Second".to_string(),
5471 annotations: None,
5472 meta: None,
5473 },
5474 ]),
5475 model: "test".to_string(),
5476 role: ContentRole::Assistant,
5477 stop_reason: None,
5478 meta: None,
5479 };
5480 assert_eq!(result.first_text(), Some("First"));
5481 }
5482
5483 #[test]
5484 fn test_create_message_result_first_text_skips_non_text() {
5485 let result = CreateMessageResult {
5486 content: SamplingContentOrArray::Array(vec![
5487 SamplingContent::Image {
5488 data: "base64data".to_string(),
5489 mime_type: "image/png".to_string(),
5490 annotations: None,
5491 meta: None,
5492 },
5493 SamplingContent::Text {
5494 text: "After image".to_string(),
5495 annotations: None,
5496 meta: None,
5497 },
5498 ]),
5499 model: "test".to_string(),
5500 role: ContentRole::Assistant,
5501 stop_reason: None,
5502 meta: None,
5503 };
5504 assert_eq!(result.first_text(), Some("After image"));
5505 }
5506
5507 #[test]
5508 fn test_create_message_result_first_text_none() {
5509 let result = CreateMessageResult {
5510 content: SamplingContentOrArray::Single(SamplingContent::Image {
5511 data: "base64data".to_string(),
5512 mime_type: "image/png".to_string(),
5513 annotations: None,
5514 meta: None,
5515 }),
5516 model: "test".to_string(),
5517 role: ContentRole::Assistant,
5518 stop_reason: None,
5519 meta: None,
5520 };
5521 assert_eq!(result.first_text(), None);
5522 }
5523
5524 #[test]
5525 fn test_tool_annotations_accessors() {
5526 let annotations = ToolAnnotations {
5527 read_only_hint: true,
5528 destructive_hint: false,
5529 idempotent_hint: true,
5530 open_world_hint: false,
5531 ..Default::default()
5532 };
5533
5534 assert!(annotations.is_read_only());
5535 assert!(!annotations.is_destructive());
5536 assert!(annotations.is_idempotent());
5537 assert!(!annotations.is_open_world());
5538 }
5539
5540 #[test]
5541 fn test_tool_annotations_defaults() {
5542 let annotations = ToolAnnotations::default();
5544
5545 assert!(!annotations.is_read_only());
5546 assert!(annotations.is_destructive());
5547 assert!(!annotations.is_idempotent());
5548 assert!(annotations.is_open_world());
5549 }
5550
5551 #[test]
5552 fn test_tool_annotations_serde_defaults() {
5553 let annotations: ToolAnnotations = serde_json::from_str("{}").unwrap();
5556
5557 assert!(!annotations.is_read_only());
5558 assert!(annotations.is_destructive());
5559 assert!(!annotations.is_idempotent());
5560 assert!(annotations.is_open_world());
5561 }
5562
5563 #[test]
5564 fn test_tool_definition_accessors_with_annotations() {
5565 let def = ToolDefinition {
5566 name: "test".to_string(),
5567 title: None,
5568 description: None,
5569 input_schema: serde_json::json!({"type": "object"}),
5570 output_schema: None,
5571 icons: None,
5572 annotations: Some(ToolAnnotations {
5573 read_only_hint: true,
5574 idempotent_hint: true,
5575 destructive_hint: false,
5576 open_world_hint: false,
5577 ..Default::default()
5578 }),
5579 execution: None,
5580 meta: None,
5581 };
5582
5583 assert!(def.is_read_only());
5584 assert!(!def.is_destructive());
5585 assert!(def.is_idempotent());
5586 assert!(!def.is_open_world());
5587 }
5588
5589 #[test]
5590 fn test_tool_definition_accessors_without_annotations() {
5591 let def = ToolDefinition {
5592 name: "test".to_string(),
5593 title: None,
5594 description: None,
5595 input_schema: serde_json::json!({"type": "object"}),
5596 output_schema: None,
5597 icons: None,
5598 annotations: None,
5599 execution: None,
5600 meta: None,
5601 };
5602
5603 assert!(!def.is_read_only());
5605 assert!(def.is_destructive());
5606 assert!(!def.is_idempotent());
5607 assert!(def.is_open_world());
5608 }
5609
5610 #[test]
5611 fn test_call_tool_result_from_list() {
5612 #[derive(serde::Serialize)]
5613 struct Item {
5614 name: String,
5615 }
5616
5617 let items = vec![
5618 Item {
5619 name: "a".to_string(),
5620 },
5621 Item {
5622 name: "b".to_string(),
5623 },
5624 Item {
5625 name: "c".to_string(),
5626 },
5627 ];
5628
5629 let result = CallToolResult::from_list("items", &items).unwrap();
5630 assert!(!result.is_error);
5631
5632 let structured = result.structured_content.unwrap();
5633 assert_eq!(structured["count"], 3);
5634 assert_eq!(structured["items"].as_array().unwrap().len(), 3);
5635 assert_eq!(structured["items"][0]["name"], "a");
5636 }
5637
5638 #[test]
5639 fn test_call_tool_result_from_list_empty() {
5640 let items: Vec<String> = vec![];
5641 let result = CallToolResult::from_list("results", &items).unwrap();
5642 assert!(!result.is_error);
5643
5644 let structured = result.structured_content.unwrap();
5645 assert_eq!(structured["count"], 0);
5646 assert_eq!(structured["results"].as_array().unwrap().len(), 0);
5647 }
5648
5649 #[test]
5654 fn test_call_tool_result_as_json() {
5655 let result = CallToolResult::json(serde_json::json!({"key": "value"}));
5656 let value = result.as_json().unwrap().unwrap();
5657 assert_eq!(value["key"], "value");
5658 }
5659
5660 #[test]
5661 fn test_call_tool_result_as_json_from_text() {
5662 let result = CallToolResult::text(r#"{"key": "value"}"#);
5663 let value = result.as_json().unwrap().unwrap();
5664 assert_eq!(value["key"], "value");
5665 }
5666
5667 #[test]
5668 fn test_call_tool_result_as_json_none() {
5669 let result = CallToolResult::text("not json");
5670 let parsed = result.as_json().unwrap();
5671 assert!(parsed.is_err());
5672 }
5673
5674 #[test]
5675 fn test_call_tool_result_deserialize() {
5676 #[derive(Debug, serde::Deserialize, PartialEq)]
5677 struct Output {
5678 key: String,
5679 }
5680
5681 let result = CallToolResult::json(serde_json::json!({"key": "value"}));
5682 let output: Output = result.deserialize().unwrap().unwrap();
5683 assert_eq!(output.key, "value");
5684 }
5685
5686 #[test]
5687 fn test_call_tool_result_as_json_empty() {
5688 let result = CallToolResult {
5689 content: vec![],
5690 is_error: false,
5691 structured_content: None,
5692 meta: None,
5693 };
5694 assert!(result.as_json().is_none());
5695 }
5696
5697 #[test]
5698 fn test_call_tool_result_deserialize_from_text() {
5699 #[derive(Debug, serde::Deserialize, PartialEq)]
5700 struct Output {
5701 key: String,
5702 }
5703
5704 let result = CallToolResult::text(r#"{"key": "from_text"}"#);
5705 let output: Output = result.deserialize().unwrap().unwrap();
5706 assert_eq!(output.key, "from_text");
5707 }
5708
5709 #[test]
5710 fn test_read_resource_result_as_json() {
5711 let result = ReadResourceResult::json("data://config", &serde_json::json!({"port": 8080}));
5712 let value = result.as_json().unwrap().unwrap();
5713 assert_eq!(value["port"], 8080);
5714 }
5715
5716 #[test]
5717 fn test_read_resource_result_deserialize() {
5718 #[derive(Debug, serde::Deserialize, PartialEq)]
5719 struct Config {
5720 port: u16,
5721 }
5722
5723 let result = ReadResourceResult::json("data://config", &serde_json::json!({"port": 8080}));
5724 let config: Config = result.deserialize().unwrap().unwrap();
5725 assert_eq!(config.port, 8080);
5726 }
5727
5728 #[test]
5729 fn test_get_prompt_result_as_json() {
5730 let result = GetPromptResult::user_message(r#"{"action": "analyze"}"#);
5731 let value = result.as_json().unwrap().unwrap();
5732 assert_eq!(value["action"], "analyze");
5733 }
5734
5735 #[test]
5736 fn test_get_prompt_result_deserialize() {
5737 #[derive(Debug, serde::Deserialize, PartialEq)]
5738 struct Params {
5739 action: String,
5740 }
5741
5742 let result = GetPromptResult::user_message(r#"{"action": "analyze"}"#);
5743 let params: Params = result.deserialize().unwrap().unwrap();
5744 assert_eq!(params.action, "analyze");
5745 }
5746
5747 #[test]
5748 fn test_get_prompt_result_as_json_empty() {
5749 let result = GetPromptResult {
5750 description: None,
5751 messages: vec![],
5752 meta: None,
5753 };
5754 assert!(result.as_json().is_none());
5755 }
5756
5757 #[test]
5758 fn test_mcp_response_serde_roundtrip() {
5759 let response = McpResponse::CallTool(CallToolResult {
5761 content: vec![Content::text("hello")],
5762 structured_content: None,
5763 is_error: false,
5764 meta: None,
5765 });
5766 let json = serde_json::to_string(&response).unwrap();
5767 let deserialized: McpResponse = serde_json::from_str(&json).unwrap();
5768 match deserialized {
5769 McpResponse::CallTool(result) => {
5770 assert_eq!(result.content[0].as_text(), Some("hello"));
5771 }
5772 _ => panic!("expected CallTool variant"),
5773 }
5774
5775 let response = McpResponse::ListTools(ListToolsResult {
5777 tools: vec![],
5778 next_cursor: Some("cursor123".to_string()),
5779 ttl_ms: None,
5780 cache_scope: None,
5781 meta: None,
5782 });
5783 let json = serde_json::to_string(&response).unwrap();
5784 let deserialized: McpResponse = serde_json::from_str(&json).unwrap();
5785 match deserialized {
5786 McpResponse::ListTools(result) => {
5787 assert_eq!(result.next_cursor.as_deref(), Some("cursor123"));
5788 }
5789 _ => panic!("expected ListTools variant"),
5790 }
5791 }
5792
5793 fn task_obj_for_tests() -> TaskObject {
5798 TaskObject {
5799 task_id: "task-786512e2".into(),
5800 status: TaskStatus::Working,
5801 status_message: None,
5802 created_at: "2025-11-25T10:30:00Z".into(),
5803 last_updated_at: "2025-11-25T10:30:00Z".into(),
5804 ttl: Some(60_000),
5805 poll_interval: Some(5_000),
5806 meta: None,
5807 }
5808 }
5809
5810 #[test]
5811 fn tasks_extension_id_matches_sep_2663() {
5812 assert_eq!(TASKS_EXTENSION_ID, "io.modelcontextprotocol/tasks");
5815 }
5816
5817 #[test]
5818 fn create_task_result_serializes_with_sep_2663_discriminator() {
5819 let result = CreateTaskResult::new(task_obj_for_tests());
5823 let json = serde_json::to_value(&result).unwrap();
5824 assert_eq!(json["resultType"], serde_json::json!("task"));
5825 assert_eq!(json["taskId"], serde_json::json!("task-786512e2"));
5827 assert_eq!(json["status"], serde_json::json!("working"));
5828 assert_eq!(json["pollInterval"], serde_json::json!(5_000));
5829 assert_eq!(json["ttl"], serde_json::json!(60_000));
5830 assert_eq!(json["createdAt"], serde_json::json!("2025-11-25T10:30:00Z"));
5831 assert_eq!(json["task"]["taskId"], serde_json::json!("task-786512e2"));
5834 }
5835
5836 #[test]
5837 fn create_task_result_round_trips_via_flat_layout() {
5838 let original = CreateTaskResult::new(task_obj_for_tests());
5839 let json = serde_json::to_string(&original).unwrap();
5840 let parsed: CreateTaskResult = serde_json::from_str(&json).unwrap();
5841 assert_eq!(parsed.task.task_id, "task-786512e2");
5842 assert_eq!(parsed.task.status, TaskStatus::Working);
5843 assert_eq!(parsed.task.ttl, Some(60_000));
5844 }
5845
5846 #[test]
5847 fn create_task_result_accepts_legacy_nested_layout() {
5848 let legacy = serde_json::json!({
5850 "task": {
5851 "taskId": "legacy-id",
5852 "status": "working",
5853 "createdAt": "2025-11-25T10:30:00Z",
5854 "lastUpdatedAt": "2025-11-25T10:30:00Z",
5855 "ttl": null
5856 }
5857 });
5858 let parsed: CreateTaskResult = serde_json::from_value(legacy).unwrap();
5859 assert_eq!(parsed.task.task_id, "legacy-id");
5860 assert_eq!(parsed.task.status, TaskStatus::Working);
5861 }
5862
5863 #[test]
5864 fn tasks_update_method_parses_via_from_jsonrpc() {
5865 let body = r#"{
5867 "jsonrpc": "2.0",
5868 "id": 6,
5869 "method": "tasks/update",
5870 "params": {
5871 "taskId": "abc-123",
5872 "inputResponses": {
5873 "name": {
5874 "action": "accept",
5875 "content": { "input": "Luca" }
5876 }
5877 }
5878 }
5879 }"#;
5880 let req: JsonRpcRequest = serde_json::from_str(body).unwrap();
5881 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
5882 match parsed {
5883 McpRequest::UpdateTask(p) => {
5884 assert_eq!(p.task_id, "abc-123");
5885 let name_resp = p.input_responses.get("name").expect("has 'name' key");
5886 assert_eq!(name_resp["action"], "accept");
5887 }
5888 other => panic!("expected UpdateTask, got {:?}", other.method_name()),
5889 }
5890 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
5892 assert_eq!(parsed.method_name(), "tasks/update");
5893 }
5894
5895 #[test]
5896 fn legacy_tasks_methods_still_parse_for_back_compat() {
5897 for (method, params, expected_name) in [
5900 (
5901 "tasks/get",
5902 serde_json::json!({ "taskId": "x" }),
5903 "tasks/get",
5904 ),
5905 (
5906 "tasks/cancel",
5907 serde_json::json!({ "taskId": "x" }),
5908 "tasks/cancel",
5909 ),
5910 (
5911 "tasks/result",
5912 serde_json::json!({ "taskId": "x" }),
5913 "tasks/result",
5914 ),
5915 ("tasks/list", serde_json::json!({}), "tasks/list"),
5916 ] {
5917 let req = JsonRpcRequest::new(1, method).with_params(params);
5918 let parsed = McpRequest::from_jsonrpc(&req)
5919 .unwrap_or_else(|e| panic!("failed to parse legacy {method}: {e:?}"));
5920 assert_eq!(parsed.method_name(), expected_name);
5921 }
5922 }
5923
5924 #[test]
5925 fn result_type_task_discriminator_constant() {
5926 assert_eq!(RESULT_TYPE_TASK, "task");
5928 assert_eq!(CreateTaskResult::RESULT_TYPE, RESULT_TYPE_TASK);
5929 }
5930
5931 #[test]
5932 fn server_capabilities_extensions_keys_match_tasks_extension_id() {
5933 let mut extensions = HashMap::new();
5937 extensions.insert(TASKS_EXTENSION_ID.to_string(), serde_json::json!({}));
5938 let caps = ServerCapabilities {
5939 extensions: Some(extensions),
5940 ..Default::default()
5941 };
5942 let json = serde_json::to_value(&caps).unwrap();
5943 assert_eq!(
5944 json["extensions"]["io.modelcontextprotocol/tasks"],
5945 serde_json::json!({})
5946 );
5947 }
5948}