1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10use crate::types::capabilities::{ClientCapabilities, ServerCapabilities};
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub struct ProtocolVersion(pub String);
15
16impl Default for ProtocolVersion {
17 fn default() -> Self {
18 Self(crate::DEFAULT_PROTOCOL_VERSION.to_string())
19 }
20}
21
22impl ProtocolVersion {
23 pub fn as_str(&self) -> &str {
25 &self.0
26 }
27}
28
29impl std::fmt::Display for ProtocolVersion {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "{}", self.0)
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct Implementation {
39 pub name: String,
41 pub version: String,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47#[serde(rename_all = "camelCase")]
48pub struct InitializeRequest {
49 pub protocol_version: String,
51 pub capabilities: ClientCapabilities,
53 pub client_info: Implementation,
55}
56
57pub type InitializeParams = InitializeRequest;
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(rename_all = "camelCase")]
63pub struct InitializeResult {
64 pub protocol_version: ProtocolVersion,
66 pub capabilities: ServerCapabilities,
68 pub server_info: Implementation,
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub instructions: Option<String>,
73}
74
75pub type Cursor = Option<String>;
77
78#[derive(Debug, Clone, Default, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct ListToolsRequest {
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub cursor: Cursor,
85}
86
87pub type ListToolsParams = ListToolsRequest;
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
92#[serde(rename_all = "camelCase")]
93pub struct ToolInfo {
94 pub name: String,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub description: Option<String>,
99 pub input_schema: Value,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105#[serde(rename_all = "camelCase")]
106pub struct ListToolsResult {
107 pub tools: Vec<ToolInfo>,
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub next_cursor: Cursor,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct CallToolRequest {
118 pub name: String,
120 #[serde(default)]
122 pub arguments: Value,
123}
124
125pub type CallToolParams = CallToolRequest;
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct CallToolResult {
132 #[serde(default)]
134 pub content: Vec<Content>,
135 #[serde(default)]
137 pub is_error: bool,
138}
139
140pub type MessageContent = Content;
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145#[serde(tag = "type", rename_all = "camelCase")]
146pub enum Content {
147 #[serde(rename_all = "camelCase")]
149 Text {
150 text: String,
152 },
153 #[serde(rename_all = "camelCase")]
155 Image {
156 data: String,
158 mime_type: String,
160 },
161 #[serde(rename_all = "camelCase")]
163 Resource {
164 uri: String,
166 #[serde(skip_serializing_if = "Option::is_none")]
168 text: Option<String>,
169 #[serde(skip_serializing_if = "Option::is_none")]
171 mime_type: Option<String>,
172 },
173}
174
175#[derive(Debug, Clone, Default, Serialize, Deserialize)]
177#[serde(rename_all = "camelCase")]
178pub struct ListPromptsRequest {
179 #[serde(skip_serializing_if = "Option::is_none")]
181 pub cursor: Cursor,
182}
183
184pub type ListPromptsParams = ListPromptsRequest;
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189#[serde(rename_all = "camelCase")]
190pub struct PromptInfo {
191 pub name: String,
193 #[serde(skip_serializing_if = "Option::is_none")]
195 pub description: Option<String>,
196 #[serde(skip_serializing_if = "Option::is_none")]
198 pub arguments: Option<Vec<PromptArgument>>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub struct PromptArgument {
205 pub name: String,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub description: Option<String>,
210 #[serde(default)]
212 pub required: bool,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub completion: Option<crate::types::completable::CompletionConfig>,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct ListPromptsResult {
222 pub prompts: Vec<PromptInfo>,
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub next_cursor: Cursor,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(rename_all = "camelCase")]
232pub struct GetPromptRequest {
233 pub name: String,
235 #[serde(default)]
237 pub arguments: HashMap<String, String>,
238}
239
240pub type GetPromptParams = GetPromptRequest;
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245#[serde(rename_all = "camelCase")]
246pub struct GetPromptResult {
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub description: Option<String>,
250 pub messages: Vec<PromptMessage>,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub struct PromptMessage {
258 pub role: Role,
260 pub content: MessageContent,
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
266#[serde(rename_all = "lowercase")]
267pub enum Role {
268 User,
270 Assistant,
272 System,
274}
275
276impl std::fmt::Display for Role {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 match self {
279 Self::User => write!(f, "user"),
280 Self::Assistant => write!(f, "assistant"),
281 Self::System => write!(f, "system"),
282 }
283 }
284}
285
286#[derive(Debug, Clone, Default, Serialize, Deserialize)]
288#[serde(rename_all = "camelCase")]
289pub struct ListResourcesRequest {
290 #[serde(skip_serializing_if = "Option::is_none")]
292 pub cursor: Cursor,
293}
294
295pub type ListResourcesParams = ListResourcesRequest;
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300#[serde(rename_all = "camelCase")]
301pub struct ResourceInfo {
302 pub uri: String,
304 pub name: String,
306 #[serde(skip_serializing_if = "Option::is_none")]
308 pub description: Option<String>,
309 #[serde(skip_serializing_if = "Option::is_none")]
311 pub mime_type: Option<String>,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
316#[serde(rename_all = "camelCase")]
317pub struct ListResourcesResult {
318 pub resources: Vec<ResourceInfo>,
320 #[serde(skip_serializing_if = "Option::is_none")]
322 pub next_cursor: Cursor,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
327#[serde(rename_all = "camelCase")]
328pub struct ReadResourceRequest {
329 pub uri: String,
331}
332
333pub type ReadResourceParams = ReadResourceRequest;
335
336#[derive(Debug, Clone, Default, Serialize, Deserialize)]
338#[serde(rename_all = "camelCase")]
339pub struct ListResourceTemplatesRequest {
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub cursor: Cursor,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347#[serde(rename_all = "camelCase")]
348pub struct ResourceTemplate {
349 pub uri_template: String,
351 pub name: String,
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub description: Option<String>,
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub mime_type: Option<String>,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
363#[serde(rename_all = "camelCase")]
364pub struct ListResourceTemplatesResult {
365 pub resource_templates: Vec<ResourceTemplate>,
367 #[serde(skip_serializing_if = "Option::is_none")]
369 pub next_cursor: Cursor,
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
374#[serde(rename_all = "camelCase")]
375pub struct SubscribeRequest {
376 pub uri: String,
378}
379
380#[derive(Debug, Clone, Serialize, Deserialize)]
382#[serde(rename_all = "camelCase")]
383pub struct UnsubscribeRequest {
384 pub uri: String,
386}
387
388#[derive(Debug, Clone, Serialize, Deserialize)]
390#[serde(rename_all = "camelCase")]
391pub struct CompleteRequest {
392 pub r#ref: CompletionReference,
394 pub argument: CompletionArgument,
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
400#[serde(tag = "type", rename_all = "camelCase")]
401pub enum CompletionReference {
402 #[serde(rename = "ref/resource")]
404 Resource {
405 uri: String,
407 },
408 #[serde(rename = "ref/prompt")]
410 Prompt {
411 name: String,
413 },
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418#[serde(rename_all = "camelCase")]
419pub struct CompletionArgument {
420 pub name: String,
422 pub value: String,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
428#[serde(rename_all = "camelCase")]
429pub struct CompleteResult {
430 pub completion: CompletionResult,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
436#[serde(rename_all = "camelCase")]
437pub struct CompletionResult {
438 pub values: Vec<String>,
440 #[serde(skip_serializing_if = "Option::is_none")]
442 pub total: Option<usize>,
443 #[serde(default)]
445 pub has_more: bool,
446}
447
448#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
450#[serde(rename_all = "lowercase")]
451pub enum LoggingLevel {
452 Debug,
454 Info,
456 Warning,
458 Error,
460 Critical,
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
466#[serde(rename_all = "camelCase")]
467pub struct ReadResourceResult {
468 pub contents: Vec<Content>,
470}
471
472#[derive(Debug, Clone, Default, Serialize, Deserialize)]
474#[serde(rename_all = "camelCase")]
475pub struct ModelPreferences {
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub hints: Option<Vec<ModelHint>>,
479 #[serde(skip_serializing_if = "Option::is_none")]
481 pub cost_priority: Option<f64>,
482 #[serde(skip_serializing_if = "Option::is_none")]
484 pub speed_priority: Option<f64>,
485 #[serde(skip_serializing_if = "Option::is_none")]
487 pub intelligence_priority: Option<f64>,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492#[serde(rename_all = "camelCase")]
493pub struct ModelHint {
494 #[serde(skip_serializing_if = "Option::is_none")]
496 pub name: Option<String>,
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize)]
501#[serde(rename_all = "camelCase")]
502pub struct ProgressNotification {
503 pub progress_token: ProgressToken,
505 pub progress: f64,
507 #[serde(skip_serializing_if = "Option::is_none")]
509 pub message: Option<String>,
510}
511
512pub type Progress = ProgressNotification;
514
515#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
517#[serde(untagged)]
518pub enum ProgressToken {
519 String(String),
521 Number(i64),
523}
524
525#[derive(Debug, Clone, Serialize, Deserialize)]
527#[serde(tag = "method", content = "params", rename_all = "camelCase")]
528pub enum ClientRequest {
529 #[serde(rename = "initialize")]
531 Initialize(InitializeParams),
532 #[serde(rename = "tools/list")]
534 ListTools(ListToolsParams),
535 #[serde(rename = "tools/call")]
537 CallTool(CallToolParams),
538 #[serde(rename = "prompts/list")]
540 ListPrompts(ListPromptsParams),
541 #[serde(rename = "prompts/get")]
543 GetPrompt(GetPromptParams),
544 #[serde(rename = "resources/list")]
546 ListResources(ListResourcesParams),
547 #[serde(rename = "resources/templates/list")]
549 ListResourceTemplates(ListResourceTemplatesRequest),
550 #[serde(rename = "resources/read")]
552 ReadResource(ReadResourceParams),
553 #[serde(rename = "resources/subscribe")]
555 Subscribe(SubscribeRequest),
556 #[serde(rename = "resources/unsubscribe")]
558 Unsubscribe(UnsubscribeRequest),
559 #[serde(rename = "completion/complete")]
561 Complete(CompleteRequest),
562 #[serde(rename = "logging/setLevel")]
564 SetLoggingLevel {
565 level: LoggingLevel,
567 },
568 #[serde(rename = "ping")]
570 Ping,
571 #[serde(rename = "sampling/createMessage")]
573 CreateMessage(CreateMessageRequest),
574 #[serde(rename = "elicitation/response")]
576 ElicitInputResponse(crate::types::elicitation::ElicitInputResponse),
577}
578
579#[derive(Debug, Clone, Serialize, Deserialize)]
581#[serde(tag = "method", content = "params", rename_all = "camelCase")]
582pub enum ServerRequest {
583 #[serde(rename = "sampling/createMessage")]
585 CreateMessage(Box<CreateMessageParams>),
586 #[serde(rename = "roots/list")]
588 ListRoots,
589 #[serde(rename = "elicitation/elicitInput")]
591 ElicitInput(Box<crate::types::elicitation::ElicitInputRequest>),
592}
593
594#[derive(Debug, Clone, Serialize, Deserialize)]
596#[serde(rename_all = "camelCase")]
597pub struct CreateMessageParams {
598 pub messages: Vec<SamplingMessage>,
600 #[serde(skip_serializing_if = "Option::is_none")]
602 pub model_preferences: Option<ModelPreferences>,
603 #[serde(skip_serializing_if = "Option::is_none")]
605 pub system_prompt: Option<String>,
606 #[serde(default)]
608 pub include_context: IncludeContext,
609 #[serde(skip_serializing_if = "Option::is_none")]
611 pub temperature: Option<f64>,
612 #[serde(skip_serializing_if = "Option::is_none")]
614 pub max_tokens: Option<u32>,
615 #[serde(skip_serializing_if = "Option::is_none")]
617 pub stop_sequences: Option<Vec<String>>,
618 #[serde(skip_serializing_if = "Option::is_none")]
620 pub metadata: Option<Value>,
621}
622
623pub type CreateMessageRequest = CreateMessageParams;
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
628#[serde(rename_all = "camelCase")]
629pub struct CreateMessageResult {
630 pub content: Content,
632 pub model: String,
634 #[serde(skip_serializing_if = "Option::is_none")]
636 pub usage: Option<TokenUsage>,
637 #[serde(skip_serializing_if = "Option::is_none")]
639 pub stop_reason: Option<String>,
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize)]
644#[serde(rename_all = "camelCase")]
645pub struct TokenUsage {
646 pub input_tokens: u32,
648 pub output_tokens: u32,
650 pub total_tokens: u32,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
656#[serde(rename_all = "camelCase")]
657pub struct SamplingMessage {
658 pub role: Role,
660 pub content: Content,
662}
663
664#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
666#[serde(rename_all = "camelCase")]
667pub enum IncludeContext {
668 All,
670 None,
672 ThisServerOnly,
674}
675
676impl Default for IncludeContext {
677 fn default() -> Self {
678 Self::None
679 }
680}
681
682#[derive(Debug, Clone, Serialize, Deserialize)]
684#[serde(tag = "method", content = "params", rename_all = "camelCase")]
685pub enum ClientNotification {
686 #[serde(rename = "notifications/initialized")]
688 Initialized,
689 #[serde(rename = "notifications/roots/list_changed")]
691 RootsListChanged,
692 #[serde(rename = "notifications/cancelled")]
694 Cancelled(CancelledParams),
695 #[serde(rename = "notifications/progress")]
697 Progress(Progress),
698}
699
700#[derive(Debug, Clone, Serialize, Deserialize)]
702#[serde(rename_all = "camelCase")]
703pub struct CancelledNotification {
704 pub request_id: crate::types::RequestId,
706 #[serde(skip_serializing_if = "Option::is_none")]
708 pub reason: Option<String>,
709}
710
711pub type CancelledParams = CancelledNotification;
713
714#[derive(Debug, Clone, Serialize, Deserialize)]
716#[serde(tag = "method", content = "params", rename_all = "camelCase")]
717pub enum ServerNotification {
718 #[serde(rename = "notifications/progress")]
720 Progress(Progress),
721 #[serde(rename = "notifications/tools/list_changed")]
723 ToolsChanged,
724 #[serde(rename = "notifications/prompts/list_changed")]
726 PromptsChanged,
727 #[serde(rename = "notifications/resources/list_changed")]
729 ResourcesChanged,
730 #[serde(rename = "notifications/roots/list_changed")]
732 RootsListChanged,
733 #[serde(rename = "notifications/resources/updated")]
735 ResourceUpdated(ResourceUpdatedParams),
736 #[serde(rename = "notifications/message")]
738 LogMessage(LogMessageParams),
739}
740
741#[derive(Debug, Clone, Serialize, Deserialize)]
743#[serde(rename_all = "camelCase")]
744pub struct ResourceUpdatedParams {
745 pub uri: String,
747}
748
749#[derive(Debug, Clone, Serialize, Deserialize)]
751#[serde(rename_all = "camelCase")]
752pub struct LogMessageParams {
753 pub level: LogLevel,
755 #[serde(skip_serializing_if = "Option::is_none")]
757 pub logger: Option<String>,
758 pub message: String,
760 #[serde(skip_serializing_if = "Option::is_none")]
762 pub data: Option<Value>,
763}
764
765#[derive(Debug, Clone, Serialize, Deserialize)]
767#[serde(untagged)]
768pub enum Request {
769 Client(Box<ClientRequest>),
771 Server(Box<ServerRequest>),
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize)]
777#[serde(untagged)]
778pub enum Notification {
779 Client(ClientNotification),
781 Server(ServerNotification),
783 Progress(ProgressNotification),
785 Cancelled(CancelledNotification),
787}
788
789#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
791#[serde(rename_all = "camelCase")]
792pub enum LogLevel {
793 Debug,
795 Info,
797 Warning,
799 Error,
801}
802
803#[cfg(test)]
804mod tests {
805 use super::*;
806 use serde_json::json;
807
808 #[test]
809 fn serialize_client_request() {
810 let req = ClientRequest::Ping;
811 let json = serde_json::to_value(&req).unwrap();
812 assert_eq!(json["method"], "ping");
813
814 let req = ClientRequest::ListTools(ListToolsParams::default());
815 let json = serde_json::to_value(&req).unwrap();
816 assert_eq!(json["method"], "tools/list");
817 }
818
819 #[test]
820 fn serialize_content() {
821 let content = Content::Text {
822 text: "Hello".to_string(),
823 };
824 let json = serde_json::to_value(&content).unwrap();
825 assert_eq!(json["type"], "text");
826 assert_eq!(json["text"], "Hello");
827 }
828
829 #[test]
830 fn tool_info_serialization() {
831 let tool = ToolInfo {
832 name: "test-tool".to_string(),
833 description: Some("A test tool".to_string()),
834 input_schema: json!({
835 "type": "object",
836 "properties": {
837 "param": {"type": "string"}
838 }
839 }),
840 };
841
842 let json = serde_json::to_value(&tool).unwrap();
843 assert_eq!(json["name"], "test-tool");
844 assert_eq!(json["description"], "A test tool");
845 assert_eq!(json["inputSchema"]["type"], "object");
846 }
847}