pmcp/types/
protocol.rs

1//! MCP protocol-specific types.
2//!
3//! This module contains all the protocol-specific request, response, and
4//! notification types defined by the MCP specification.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10use crate::types::capabilities::{ClientCapabilities, ServerCapabilities};
11
12/// Protocol version identifier.
13#[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    /// Get the version as a string slice.
24    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/// Implementation information.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct Implementation {
39    /// Implementation name (e.g., "mcp-sdk-rust")
40    pub name: String,
41    /// Implementation version
42    pub version: String,
43}
44
45/// Initialize request.
46#[derive(Debug, Clone, Serialize, Deserialize)]
47#[serde(rename_all = "camelCase")]
48pub struct InitializeRequest {
49    /// Protocol version the client wants to use
50    pub protocol_version: String,
51    /// Client capabilities
52    pub capabilities: ClientCapabilities,
53    /// Client implementation info
54    pub client_info: Implementation,
55}
56
57/// Initialize request parameters (legacy name).
58pub type InitializeParams = InitializeRequest;
59
60/// Initialize response.
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(rename_all = "camelCase")]
63pub struct InitializeResult {
64    /// Negotiated protocol version
65    pub protocol_version: ProtocolVersion,
66    /// Server capabilities
67    pub capabilities: ServerCapabilities,
68    /// Server implementation info
69    pub server_info: Implementation,
70    /// Optional instructions
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub instructions: Option<String>,
73}
74
75/// Pagination cursor.
76pub type Cursor = Option<String>;
77
78/// List tools request.
79#[derive(Debug, Clone, Default, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct ListToolsRequest {
82    /// Pagination cursor
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub cursor: Cursor,
85}
86
87/// List tools params (legacy name).
88pub type ListToolsParams = ListToolsRequest;
89
90/// Tool information.
91#[derive(Debug, Clone, Serialize, Deserialize)]
92#[serde(rename_all = "camelCase")]
93pub struct ToolInfo {
94    /// Tool name (unique identifier)
95    pub name: String,
96    /// Human-readable description
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub description: Option<String>,
99    /// JSON Schema for tool parameters
100    pub input_schema: Value,
101}
102
103/// List tools response.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[serde(rename_all = "camelCase")]
106pub struct ListToolsResult {
107    /// Available tools
108    pub tools: Vec<ToolInfo>,
109    /// Pagination cursor for next page
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub next_cursor: Cursor,
112}
113
114/// Tool call request.
115#[derive(Debug, Clone, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct CallToolRequest {
118    /// Tool name to invoke
119    pub name: String,
120    /// Tool arguments (must match input schema)
121    #[serde(default)]
122    pub arguments: Value,
123}
124
125/// Tool call parameters (legacy name).
126pub type CallToolParams = CallToolRequest;
127
128/// Tool call result.
129#[derive(Debug, Clone, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct CallToolResult {
132    /// Tool execution result
133    #[serde(default)]
134    pub content: Vec<Content>,
135    /// Whether the tool call represents an error
136    #[serde(default)]
137    pub is_error: bool,
138}
139
140/// Message content type alias.
141pub type MessageContent = Content;
142
143/// Content item in responses.
144#[derive(Debug, Clone, Serialize, Deserialize)]
145#[serde(tag = "type", rename_all = "camelCase")]
146pub enum Content {
147    /// Text content
148    #[serde(rename_all = "camelCase")]
149    Text {
150        /// The text content
151        text: String,
152    },
153    /// Image content
154    #[serde(rename_all = "camelCase")]
155    Image {
156        /// Base64-encoded image data
157        data: String,
158        /// MIME type (e.g., "image/png")
159        mime_type: String,
160    },
161    /// Resource reference
162    #[serde(rename_all = "camelCase")]
163    Resource {
164        /// Resource URI
165        uri: String,
166        /// Optional resource content
167        #[serde(skip_serializing_if = "Option::is_none")]
168        text: Option<String>,
169        /// MIME type
170        #[serde(skip_serializing_if = "Option::is_none")]
171        mime_type: Option<String>,
172    },
173}
174
175/// List prompts request.
176#[derive(Debug, Clone, Default, Serialize, Deserialize)]
177#[serde(rename_all = "camelCase")]
178pub struct ListPromptsRequest {
179    /// Pagination cursor
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub cursor: Cursor,
182}
183
184/// List prompts params (legacy name).
185pub type ListPromptsParams = ListPromptsRequest;
186
187/// Prompt information.
188#[derive(Debug, Clone, Serialize, Deserialize)]
189#[serde(rename_all = "camelCase")]
190pub struct PromptInfo {
191    /// Prompt name (unique identifier)
192    pub name: String,
193    /// Human-readable description
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub description: Option<String>,
196    /// Prompt arguments schema
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub arguments: Option<Vec<PromptArgument>>,
199}
200
201/// Prompt argument definition.
202#[derive(Debug, Clone, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub struct PromptArgument {
205    /// Argument name
206    pub name: String,
207    /// Human-readable description
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub description: Option<String>,
210    /// Whether the argument is required
211    #[serde(default)]
212    pub required: bool,
213    /// Completion configuration for this argument
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub completion: Option<crate::types::completable::CompletionConfig>,
216}
217
218/// List prompts response.
219#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct ListPromptsResult {
222    /// Available prompts
223    pub prompts: Vec<PromptInfo>,
224    /// Pagination cursor
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub next_cursor: Cursor,
227}
228
229/// Get prompt request.
230#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(rename_all = "camelCase")]
232pub struct GetPromptRequest {
233    /// Prompt name
234    pub name: String,
235    /// Prompt arguments
236    #[serde(default)]
237    pub arguments: HashMap<String, String>,
238}
239
240/// Get prompt params (legacy name).
241pub type GetPromptParams = GetPromptRequest;
242
243/// Get prompt result.
244#[derive(Debug, Clone, Serialize, Deserialize)]
245#[serde(rename_all = "camelCase")]
246pub struct GetPromptResult {
247    /// Prompt description
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub description: Option<String>,
250    /// Prompt messages
251    pub messages: Vec<PromptMessage>,
252}
253
254/// Message in a prompt.
255#[derive(Debug, Clone, Serialize, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub struct PromptMessage {
258    /// Message role
259    pub role: Role,
260    /// Message content
261    pub content: MessageContent,
262}
263
264/// Message role.
265#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
266#[serde(rename_all = "lowercase")]
267pub enum Role {
268    /// User message
269    User,
270    /// Assistant message
271    Assistant,
272    /// System message
273    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/// List resources request.
287#[derive(Debug, Clone, Default, Serialize, Deserialize)]
288#[serde(rename_all = "camelCase")]
289pub struct ListResourcesRequest {
290    /// Pagination cursor
291    #[serde(skip_serializing_if = "Option::is_none")]
292    pub cursor: Cursor,
293}
294
295/// List resources params (legacy name).
296pub type ListResourcesParams = ListResourcesRequest;
297
298/// Resource information.
299#[derive(Debug, Clone, Serialize, Deserialize)]
300#[serde(rename_all = "camelCase")]
301pub struct ResourceInfo {
302    /// Resource URI
303    pub uri: String,
304    /// Human-readable name
305    pub name: String,
306    /// Resource description
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub description: Option<String>,
309    /// MIME type
310    #[serde(skip_serializing_if = "Option::is_none")]
311    pub mime_type: Option<String>,
312}
313
314/// List resources response.
315#[derive(Debug, Clone, Serialize, Deserialize)]
316#[serde(rename_all = "camelCase")]
317pub struct ListResourcesResult {
318    /// Available resources
319    pub resources: Vec<ResourceInfo>,
320    /// Pagination cursor
321    #[serde(skip_serializing_if = "Option::is_none")]
322    pub next_cursor: Cursor,
323}
324
325/// Read resource request.
326#[derive(Debug, Clone, Serialize, Deserialize)]
327#[serde(rename_all = "camelCase")]
328pub struct ReadResourceRequest {
329    /// Resource URI
330    pub uri: String,
331}
332
333/// Read resource params (legacy name).
334pub type ReadResourceParams = ReadResourceRequest;
335
336/// List resource templates request.
337#[derive(Debug, Clone, Default, Serialize, Deserialize)]
338#[serde(rename_all = "camelCase")]
339pub struct ListResourceTemplatesRequest {
340    /// Pagination cursor
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub cursor: Cursor,
343}
344
345/// Resource template.
346#[derive(Debug, Clone, Serialize, Deserialize)]
347#[serde(rename_all = "camelCase")]
348pub struct ResourceTemplate {
349    /// Template URI pattern
350    pub uri_template: String,
351    /// Template name
352    pub name: String,
353    /// Template description
354    #[serde(skip_serializing_if = "Option::is_none")]
355    pub description: Option<String>,
356    /// MIME type for resources created from this template
357    #[serde(skip_serializing_if = "Option::is_none")]
358    pub mime_type: Option<String>,
359}
360
361/// List resource templates result.
362#[derive(Debug, Clone, Serialize, Deserialize)]
363#[serde(rename_all = "camelCase")]
364pub struct ListResourceTemplatesResult {
365    /// Available resource templates
366    pub resource_templates: Vec<ResourceTemplate>,
367    /// Pagination cursor
368    #[serde(skip_serializing_if = "Option::is_none")]
369    pub next_cursor: Cursor,
370}
371
372/// Subscribe to resource request.
373#[derive(Debug, Clone, Serialize, Deserialize)]
374#[serde(rename_all = "camelCase")]
375pub struct SubscribeRequest {
376    /// Resource URI to subscribe to
377    pub uri: String,
378}
379
380/// Unsubscribe from resource request.
381#[derive(Debug, Clone, Serialize, Deserialize)]
382#[serde(rename_all = "camelCase")]
383pub struct UnsubscribeRequest {
384    /// Resource URI to unsubscribe from
385    pub uri: String,
386}
387
388/// Completion request.
389#[derive(Debug, Clone, Serialize, Deserialize)]
390#[serde(rename_all = "camelCase")]
391pub struct CompleteRequest {
392    /// The reference to complete from
393    pub r#ref: CompletionReference,
394    /// The argument to complete
395    pub argument: CompletionArgument,
396}
397
398/// Completion reference.
399#[derive(Debug, Clone, Serialize, Deserialize)]
400#[serde(tag = "type", rename_all = "camelCase")]
401pub enum CompletionReference {
402    /// Complete from a resource
403    #[serde(rename = "ref/resource")]
404    Resource {
405        /// Resource URI
406        uri: String,
407    },
408    /// Complete from a prompt
409    #[serde(rename = "ref/prompt")]
410    Prompt {
411        /// Prompt name
412        name: String,
413    },
414}
415
416/// Completion argument.
417#[derive(Debug, Clone, Serialize, Deserialize)]
418#[serde(rename_all = "camelCase")]
419pub struct CompletionArgument {
420    /// Argument name
421    pub name: String,
422    /// Argument value
423    pub value: String,
424}
425
426/// Completion result.
427#[derive(Debug, Clone, Serialize, Deserialize)]
428#[serde(rename_all = "camelCase")]
429pub struct CompleteResult {
430    /// Completion options
431    pub completion: CompletionResult,
432}
433
434/// Completion result.
435#[derive(Debug, Clone, Serialize, Deserialize)]
436#[serde(rename_all = "camelCase")]
437pub struct CompletionResult {
438    /// Suggested values
439    pub values: Vec<String>,
440    /// Total number of completions available
441    #[serde(skip_serializing_if = "Option::is_none")]
442    pub total: Option<usize>,
443    /// Whether there are more completions available
444    #[serde(default)]
445    pub has_more: bool,
446}
447
448/// Logging level.
449#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
450#[serde(rename_all = "lowercase")]
451pub enum LoggingLevel {
452    /// Debug messages
453    Debug,
454    /// Informational messages
455    Info,
456    /// Warnings
457    Warning,
458    /// Errors
459    Error,
460    /// Critical errors
461    Critical,
462}
463
464/// Read resource result.
465#[derive(Debug, Clone, Serialize, Deserialize)]
466#[serde(rename_all = "camelCase")]
467pub struct ReadResourceResult {
468    /// Resource contents
469    pub contents: Vec<Content>,
470}
471
472/// Model preferences for sampling.
473#[derive(Debug, Clone, Default, Serialize, Deserialize)]
474#[serde(rename_all = "camelCase")]
475pub struct ModelPreferences {
476    /// Hints for model selection
477    #[serde(skip_serializing_if = "Option::is_none")]
478    pub hints: Option<Vec<ModelHint>>,
479    /// Cost priority (0-1, higher = more important)
480    #[serde(skip_serializing_if = "Option::is_none")]
481    pub cost_priority: Option<f64>,
482    /// Speed priority (0-1, higher = more important)
483    #[serde(skip_serializing_if = "Option::is_none")]
484    pub speed_priority: Option<f64>,
485    /// Intelligence priority (0-1, higher = more important)
486    #[serde(skip_serializing_if = "Option::is_none")]
487    pub intelligence_priority: Option<f64>,
488}
489
490/// Model hint for sampling.
491#[derive(Debug, Clone, Serialize, Deserialize)]
492#[serde(rename_all = "camelCase")]
493pub struct ModelHint {
494    /// Model name/identifier hint
495    #[serde(skip_serializing_if = "Option::is_none")]
496    pub name: Option<String>,
497}
498
499/// Progress notification.
500#[derive(Debug, Clone, Serialize, Deserialize)]
501#[serde(rename_all = "camelCase")]
502pub struct ProgressNotification {
503    /// Progress token from the original request
504    pub progress_token: ProgressToken,
505    /// Progress percentage (0-100)
506    pub progress: f64,
507    /// Optional progress message
508    #[serde(skip_serializing_if = "Option::is_none")]
509    pub message: Option<String>,
510}
511
512/// Progress (legacy alias).
513pub type Progress = ProgressNotification;
514
515/// Progress token type.
516#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
517#[serde(untagged)]
518pub enum ProgressToken {
519    /// String token
520    String(String),
521    /// Numeric token
522    Number(i64),
523}
524
525/// Client request types.
526#[derive(Debug, Clone, Serialize, Deserialize)]
527#[serde(tag = "method", content = "params", rename_all = "camelCase")]
528pub enum ClientRequest {
529    /// Initialize the connection
530    #[serde(rename = "initialize")]
531    Initialize(InitializeParams),
532    /// List available tools
533    #[serde(rename = "tools/list")]
534    ListTools(ListToolsParams),
535    /// Call a tool
536    #[serde(rename = "tools/call")]
537    CallTool(CallToolParams),
538    /// List available prompts
539    #[serde(rename = "prompts/list")]
540    ListPrompts(ListPromptsParams),
541    /// Get a prompt
542    #[serde(rename = "prompts/get")]
543    GetPrompt(GetPromptParams),
544    /// List available resources
545    #[serde(rename = "resources/list")]
546    ListResources(ListResourcesParams),
547    /// List resource templates
548    #[serde(rename = "resources/templates/list")]
549    ListResourceTemplates(ListResourceTemplatesRequest),
550    /// Read a resource
551    #[serde(rename = "resources/read")]
552    ReadResource(ReadResourceParams),
553    /// Subscribe to resource updates
554    #[serde(rename = "resources/subscribe")]
555    Subscribe(SubscribeRequest),
556    /// Unsubscribe from resource updates
557    #[serde(rename = "resources/unsubscribe")]
558    Unsubscribe(UnsubscribeRequest),
559    /// Request completion
560    #[serde(rename = "completion/complete")]
561    Complete(CompleteRequest),
562    /// Set logging level
563    #[serde(rename = "logging/setLevel")]
564    SetLoggingLevel {
565        /// Logging level to set
566        level: LoggingLevel,
567    },
568    /// Ping request
569    #[serde(rename = "ping")]
570    Ping,
571    /// Create message (sampling)
572    #[serde(rename = "sampling/createMessage")]
573    CreateMessage(CreateMessageRequest),
574    /// Response to elicitation request
575    #[serde(rename = "elicitation/response")]
576    ElicitInputResponse(crate::types::elicitation::ElicitInputResponse),
577}
578
579/// Server request types.
580#[derive(Debug, Clone, Serialize, Deserialize)]
581#[serde(tag = "method", content = "params", rename_all = "camelCase")]
582pub enum ServerRequest {
583    /// Request to create a message (sampling)
584    #[serde(rename = "sampling/createMessage")]
585    CreateMessage(Box<CreateMessageParams>),
586    /// List roots request
587    #[serde(rename = "roots/list")]
588    ListRoots,
589    /// Elicit input from user
590    #[serde(rename = "elicitation/elicitInput")]
591    ElicitInput(Box<crate::types::elicitation::ElicitInputRequest>),
592}
593
594/// Create message parameters (for server requests).
595#[derive(Debug, Clone, Serialize, Deserialize)]
596#[serde(rename_all = "camelCase")]
597pub struct CreateMessageParams {
598    /// Messages to sample from
599    pub messages: Vec<SamplingMessage>,
600    /// Optional model preferences
601    #[serde(skip_serializing_if = "Option::is_none")]
602    pub model_preferences: Option<ModelPreferences>,
603    /// Optional system prompt
604    #[serde(skip_serializing_if = "Option::is_none")]
605    pub system_prompt: Option<String>,
606    /// Include context from MCP
607    #[serde(default)]
608    pub include_context: IncludeContext,
609    /// Temperature (0-1)
610    #[serde(skip_serializing_if = "Option::is_none")]
611    pub temperature: Option<f64>,
612    /// Maximum tokens to generate
613    #[serde(skip_serializing_if = "Option::is_none")]
614    pub max_tokens: Option<u32>,
615    /// Stop sequences
616    #[serde(skip_serializing_if = "Option::is_none")]
617    pub stop_sequences: Option<Vec<String>>,
618    /// Additional model-specific parameters
619    #[serde(skip_serializing_if = "Option::is_none")]
620    pub metadata: Option<Value>,
621}
622
623/// Create message request (for client requests).
624pub type CreateMessageRequest = CreateMessageParams;
625
626/// Create message result.
627#[derive(Debug, Clone, Serialize, Deserialize)]
628#[serde(rename_all = "camelCase")]
629pub struct CreateMessageResult {
630    /// The content generated by the model
631    pub content: Content,
632    /// The model used for generation
633    pub model: String,
634    /// Token usage information
635    #[serde(skip_serializing_if = "Option::is_none")]
636    pub usage: Option<TokenUsage>,
637    /// Stop reason
638    #[serde(skip_serializing_if = "Option::is_none")]
639    pub stop_reason: Option<String>,
640}
641
642/// Token usage information.
643#[derive(Debug, Clone, Serialize, Deserialize)]
644#[serde(rename_all = "camelCase")]
645pub struct TokenUsage {
646    /// Input tokens used
647    pub input_tokens: u32,
648    /// Output tokens generated
649    pub output_tokens: u32,
650    /// Total tokens used
651    pub total_tokens: u32,
652}
653
654/// Sampling message.
655#[derive(Debug, Clone, Serialize, Deserialize)]
656#[serde(rename_all = "camelCase")]
657pub struct SamplingMessage {
658    /// Message role
659    pub role: Role,
660    /// Message content
661    pub content: Content,
662}
663
664/// Context to include in sampling.
665#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
666#[serde(rename_all = "camelCase")]
667pub enum IncludeContext {
668    /// Include all context
669    All,
670    /// Include no context
671    None,
672    /// Include specific context types
673    ThisServerOnly,
674}
675
676impl Default for IncludeContext {
677    fn default() -> Self {
678        Self::None
679    }
680}
681
682/// Client notification types.
683#[derive(Debug, Clone, Serialize, Deserialize)]
684#[serde(tag = "method", content = "params", rename_all = "camelCase")]
685pub enum ClientNotification {
686    /// Notification that client has been initialized
687    #[serde(rename = "notifications/initialized")]
688    Initialized,
689    /// Notification that roots have changed
690    #[serde(rename = "notifications/roots/list_changed")]
691    RootsListChanged,
692    /// Notification that a request was cancelled
693    #[serde(rename = "notifications/cancelled")]
694    Cancelled(CancelledParams),
695    /// Progress update
696    #[serde(rename = "notifications/progress")]
697    Progress(Progress),
698}
699
700/// Cancelled notification.
701#[derive(Debug, Clone, Serialize, Deserialize)]
702#[serde(rename_all = "camelCase")]
703pub struct CancelledNotification {
704    /// The request ID that was cancelled
705    pub request_id: crate::types::RequestId,
706    /// Optional reason for cancellation
707    #[serde(skip_serializing_if = "Option::is_none")]
708    pub reason: Option<String>,
709}
710
711/// Cancelled params (legacy alias).
712pub type CancelledParams = CancelledNotification;
713
714/// Server notification types.
715#[derive(Debug, Clone, Serialize, Deserialize)]
716#[serde(tag = "method", content = "params", rename_all = "camelCase")]
717pub enum ServerNotification {
718    /// Progress update
719    #[serde(rename = "notifications/progress")]
720    Progress(Progress),
721    /// Tools have changed
722    #[serde(rename = "notifications/tools/list_changed")]
723    ToolsChanged,
724    /// Prompts have changed
725    #[serde(rename = "notifications/prompts/list_changed")]
726    PromptsChanged,
727    /// Resources have changed
728    #[serde(rename = "notifications/resources/list_changed")]
729    ResourcesChanged,
730    /// Roots have changed
731    #[serde(rename = "notifications/roots/list_changed")]
732    RootsListChanged,
733    /// Resource was updated
734    #[serde(rename = "notifications/resources/updated")]
735    ResourceUpdated(ResourceUpdatedParams),
736    /// Log message
737    #[serde(rename = "notifications/message")]
738    LogMessage(LogMessageParams),
739}
740
741/// Resource updated notification.
742#[derive(Debug, Clone, Serialize, Deserialize)]
743#[serde(rename_all = "camelCase")]
744pub struct ResourceUpdatedParams {
745    /// Resource URI that was updated
746    pub uri: String,
747}
748
749/// Log message notification.
750#[derive(Debug, Clone, Serialize, Deserialize)]
751#[serde(rename_all = "camelCase")]
752pub struct LogMessageParams {
753    /// Log level
754    pub level: LogLevel,
755    /// Logger name/category
756    #[serde(skip_serializing_if = "Option::is_none")]
757    pub logger: Option<String>,
758    /// Log message
759    pub message: String,
760    /// Additional data
761    #[serde(skip_serializing_if = "Option::is_none")]
762    pub data: Option<Value>,
763}
764
765/// Combined request types (client or server).
766#[derive(Debug, Clone, Serialize, Deserialize)]
767#[serde(untagged)]
768pub enum Request {
769    /// Client request
770    Client(Box<ClientRequest>),
771    /// Server request
772    Server(Box<ServerRequest>),
773}
774
775/// Combined notification types (client or server).
776#[derive(Debug, Clone, Serialize, Deserialize)]
777#[serde(untagged)]
778pub enum Notification {
779    /// Client notification
780    Client(ClientNotification),
781    /// Server notification  
782    Server(ServerNotification),
783    /// Progress notification
784    Progress(ProgressNotification),
785    /// Cancelled notification
786    Cancelled(CancelledNotification),
787}
788
789/// Log level.
790#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
791#[serde(rename_all = "camelCase")]
792pub enum LogLevel {
793    /// Debug level
794    Debug,
795    /// Info level
796    Info,
797    /// Warning level
798    Warning,
799    /// Error level
800    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}