pulseengine_mcp_protocol/
model.rs

1//! MCP model types for protocol messages and data structures
2
3use crate::Error;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7/// JSON-RPC 2.0 Request
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Request {
10    /// JSON-RPC version (always "2.0")
11    pub jsonrpc: String,
12    /// Request method name
13    pub method: String,
14    /// Request parameters
15    #[serde(default = "serde_json::Value::default")]
16    pub params: serde_json::Value,
17    /// Request ID (missing for notifications)
18    #[serde(default = "default_null")]
19    pub id: serde_json::Value,
20}
21
22fn default_null() -> serde_json::Value {
23    serde_json::Value::Null
24}
25
26/// JSON-RPC 2.0 Response
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct Response {
29    /// JSON-RPC version (always "2.0")
30    pub jsonrpc: String,
31    /// Response result (if successful)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub result: Option<serde_json::Value>,
34    /// Response error (if failed)
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub error: Option<Error>,
37    /// Request ID
38    pub id: serde_json::Value,
39}
40
41/// Protocol version information
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct ProtocolVersion {
44    pub major: u32,
45    pub minor: u32,
46    pub patch: u32,
47}
48
49impl Default for ProtocolVersion {
50    fn default() -> Self {
51        Self {
52            major: 2025,
53            minor: 6,
54            patch: 18,
55        }
56    }
57}
58
59impl std::fmt::Display for ProtocolVersion {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(f, "{:04}-{:02}-{:02}", self.major, self.minor, self.patch)
62    }
63}
64
65/// Server implementation information
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct Implementation {
68    pub name: String,
69    pub version: String,
70}
71
72/// Server capabilities configuration
73#[derive(Debug, Clone, Serialize, Deserialize, Default)]
74pub struct ServerCapabilities {
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub tools: Option<ToolsCapability>,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub resources: Option<ResourcesCapability>,
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub prompts: Option<PromptsCapability>,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub logging: Option<LoggingCapability>,
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub sampling: Option<SamplingCapability>,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub elicitation: Option<ElicitationCapability>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, Default)]
90pub struct ToolsCapability {
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub list_changed: Option<bool>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, Default)]
96pub struct ResourcesCapability {
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub subscribe: Option<bool>,
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub list_changed: Option<bool>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, Default)]
104pub struct PromptsCapability {
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub list_changed: Option<bool>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize, Default)]
110pub struct LoggingCapability {
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub level: Option<String>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, Default)]
116pub struct SamplingCapability {}
117
118#[derive(Debug, Clone, Serialize, Deserialize, Default)]
119pub struct ElicitationCapability {}
120
121impl ServerCapabilities {
122    pub fn builder() -> ServerCapabilitiesBuilder {
123        ServerCapabilitiesBuilder::default()
124    }
125}
126
127#[derive(Default)]
128pub struct ServerCapabilitiesBuilder {
129    capabilities: ServerCapabilities,
130}
131
132impl ServerCapabilitiesBuilder {
133    #[must_use]
134    pub fn enable_tools(mut self) -> Self {
135        self.capabilities.tools = Some(ToolsCapability {
136            list_changed: Some(true),
137        });
138        self
139    }
140
141    #[must_use]
142    pub fn enable_resources(mut self) -> Self {
143        self.capabilities.resources = Some(ResourcesCapability {
144            subscribe: Some(true),
145            list_changed: Some(true),
146        });
147        self
148    }
149
150    #[must_use]
151    pub fn enable_prompts(mut self) -> Self {
152        self.capabilities.prompts = Some(PromptsCapability {
153            list_changed: Some(true),
154        });
155        self
156    }
157
158    #[must_use]
159    pub fn enable_logging(mut self) -> Self {
160        self.capabilities.logging = Some(LoggingCapability {
161            level: Some("info".to_string()),
162        });
163        self
164    }
165
166    #[must_use]
167    pub fn enable_sampling(mut self) -> Self {
168        self.capabilities.sampling = Some(SamplingCapability {});
169        self
170    }
171
172    #[must_use]
173    pub fn enable_elicitation(mut self) -> Self {
174        self.capabilities.elicitation = Some(ElicitationCapability {});
175        self
176    }
177
178    pub fn build(self) -> ServerCapabilities {
179        self.capabilities
180    }
181}
182
183/// Server information response
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct ServerInfo {
186    pub protocol_version: ProtocolVersion,
187    pub capabilities: ServerCapabilities,
188    pub server_info: Implementation,
189    pub instructions: Option<String>,
190}
191
192/// Tool definition
193#[derive(Debug, Clone, Serialize, Deserialize)]
194#[serde(rename_all = "camelCase")]
195pub struct Tool {
196    pub name: String,
197    pub description: String,
198    pub input_schema: serde_json::Value,
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub output_schema: Option<serde_json::Value>,
201}
202
203/// List tools result
204#[derive(Debug, Clone, Serialize, Deserialize)]
205#[serde(rename_all = "camelCase")]
206pub struct ListToolsResult {
207    pub tools: Vec<Tool>,
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub next_cursor: Option<String>,
210}
211
212/// Pagination parameters
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct PaginatedRequestParam {
215    pub cursor: Option<String>,
216}
217
218/// Tool call parameters
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct CallToolRequestParam {
221    pub name: String,
222    pub arguments: Option<serde_json::Value>,
223}
224
225/// Content types for tool responses
226#[derive(Debug, Clone, Serialize, Deserialize)]
227#[serde(tag = "type")]
228pub enum Content {
229    #[serde(rename = "text")]
230    Text { text: String },
231    #[serde(rename = "image")]
232    Image { data: String, mime_type: String },
233    #[serde(rename = "resource")]
234    Resource {
235        resource: String,
236        text: Option<String>,
237    },
238}
239
240impl Content {
241    pub fn text(text: impl Into<String>) -> Self {
242        Self::Text { text: text.into() }
243    }
244
245    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
246        Self::Image {
247            data: data.into(),
248            mime_type: mime_type.into(),
249        }
250    }
251
252    pub fn resource(resource: impl Into<String>, text: Option<String>) -> Self {
253        Self::Resource {
254            resource: resource.into(),
255            text,
256        }
257    }
258
259    /// Get text content if this is a text content type
260    pub fn as_text(&self) -> Option<&Self> {
261        match self {
262            Self::Text { .. } => Some(self),
263            _ => None,
264        }
265    }
266}
267
268/// Text content struct for compatibility
269pub struct TextContent {
270    pub text: String,
271}
272
273impl Content {
274    /// Get text content as `TextContent` struct for compatibility
275    pub fn as_text_content(&self) -> Option<TextContent> {
276        match self {
277            Self::Text { text } => Some(TextContent { text: text.clone() }),
278            _ => None,
279        }
280    }
281}
282
283/// Tool call result
284#[derive(Debug, Clone, Serialize, Deserialize)]
285#[serde(rename_all = "camelCase")]
286pub struct CallToolResult {
287    pub content: Vec<Content>,
288    pub is_error: Option<bool>,
289    #[serde(skip_serializing_if = "Option::is_none")]
290    pub structured_content: Option<serde_json::Value>,
291}
292
293impl CallToolResult {
294    pub fn success(content: Vec<Content>) -> Self {
295        Self {
296            content,
297            is_error: Some(false),
298            structured_content: None,
299        }
300    }
301
302    pub fn error(content: Vec<Content>) -> Self {
303        Self {
304            content,
305            is_error: Some(true),
306            structured_content: None,
307        }
308    }
309
310    pub fn text(text: impl Into<String>) -> Self {
311        Self::success(vec![Content::text(text)])
312    }
313
314    pub fn error_text(text: impl Into<String>) -> Self {
315        Self::error(vec![Content::text(text)])
316    }
317
318    /// Create a success result with structured content
319    pub fn structured(content: Vec<Content>, structured_content: serde_json::Value) -> Self {
320        Self {
321            content,
322            is_error: Some(false),
323            structured_content: Some(structured_content),
324        }
325    }
326
327    /// Create an error result with structured content
328    pub fn structured_error(content: Vec<Content>, structured_content: serde_json::Value) -> Self {
329        Self {
330            content,
331            is_error: Some(true),
332            structured_content: Some(structured_content),
333        }
334    }
335
336    /// Create a result with both text and structured content
337    pub fn text_with_structured(
338        text: impl Into<String>,
339        structured_content: serde_json::Value,
340    ) -> Self {
341        Self::structured(vec![Content::text(text)], structured_content)
342    }
343
344    /// Validate structured content against a schema
345    ///
346    /// # Errors
347    ///
348    /// Returns an error if the structured content doesn't match the provided schema
349    pub fn validate_structured_content(
350        &self,
351        output_schema: &serde_json::Value,
352    ) -> crate::Result<()> {
353        use crate::validation::Validator;
354
355        if let Some(structured_content) = &self.structured_content {
356            Validator::validate_structured_content(structured_content, output_schema)?;
357        }
358        Ok(())
359    }
360}
361
362/// Resource definition
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct Resource {
365    pub uri: String,
366    pub name: String,
367    pub description: Option<String>,
368    pub mime_type: Option<String>,
369    pub annotations: Option<Annotations>,
370    #[serde(skip_serializing_if = "Option::is_none")]
371    pub raw: Option<RawResource>,
372}
373
374/// Resource annotations
375#[derive(Debug, Clone, Serialize, Deserialize, Default)]
376pub struct Annotations {
377    pub audience: Option<Vec<String>>,
378    pub priority: Option<f32>,
379}
380
381/// List resources result
382#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct ListResourcesResult {
384    pub resources: Vec<Resource>,
385    #[serde(skip_serializing_if = "Option::is_none")]
386    pub next_cursor: Option<String>,
387}
388
389/// Read resource parameters
390#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct ReadResourceRequestParam {
392    pub uri: String,
393}
394
395/// Resource contents wrapper
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct ResourceContents {
398    pub uri: String,
399    pub mime_type: Option<String>,
400    pub text: Option<String>,
401    pub blob: Option<String>,
402}
403
404/// Read resource result
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct ReadResourceResult {
407    pub contents: Vec<ResourceContents>,
408}
409
410/// Raw resource (for internal use)
411#[derive(Debug, Clone, Serialize, Deserialize)]
412pub struct RawResource {
413    pub uri: String,
414    pub data: Vec<u8>,
415    pub mime_type: Option<String>,
416    pub name: Option<String>,
417    pub description: Option<String>,
418    pub size: Option<usize>,
419}
420
421impl PromptMessage {
422    /// Create a new text message
423    pub fn new_text(role: PromptMessageRole, text: impl Into<String>) -> Self {
424        Self {
425            role,
426            content: PromptMessageContent::Text { text: text.into() },
427        }
428    }
429
430    /// Create a new image message
431    pub fn new_image(
432        role: PromptMessageRole,
433        data: impl Into<String>,
434        mime_type: impl Into<String>,
435    ) -> Self {
436        Self {
437            role,
438            content: PromptMessageContent::Image {
439                data: data.into(),
440                mime_type: mime_type.into(),
441            },
442        }
443    }
444}
445
446impl CompleteResult {
447    /// Create a simple completion result
448    pub fn simple(completion: impl Into<String>) -> Self {
449        Self {
450            completion: vec![CompletionInfo {
451                completion: completion.into(),
452                has_more: Some(false),
453            }],
454        }
455    }
456}
457
458/// Prompt definition
459#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct Prompt {
461    pub name: String,
462    pub description: Option<String>,
463    pub arguments: Option<Vec<PromptArgument>>,
464}
465
466/// Prompt argument definition
467#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct PromptArgument {
469    pub name: String,
470    pub description: Option<String>,
471    pub required: Option<bool>,
472}
473
474/// List prompts result
475#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct ListPromptsResult {
477    pub prompts: Vec<Prompt>,
478    #[serde(skip_serializing_if = "Option::is_none")]
479    pub next_cursor: Option<String>,
480}
481
482/// Get prompt parameters
483#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct GetPromptRequestParam {
485    pub name: String,
486    pub arguments: Option<HashMap<String, String>>,
487}
488
489/// Prompt message role
490#[derive(Debug, Clone, Serialize, Deserialize)]
491#[serde(rename_all = "lowercase")]
492pub enum PromptMessageRole {
493    User,
494    Assistant,
495    System,
496}
497
498/// Prompt message content
499#[derive(Debug, Clone, Serialize, Deserialize)]
500#[serde(tag = "type")]
501pub enum PromptMessageContent {
502    #[serde(rename = "text")]
503    Text { text: String },
504    #[serde(rename = "image")]
505    Image { data: String, mime_type: String },
506}
507
508/// Prompt message
509#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct PromptMessage {
511    pub role: PromptMessageRole,
512    pub content: PromptMessageContent,
513}
514
515/// Get prompt result
516#[derive(Debug, Clone, Serialize, Deserialize)]
517pub struct GetPromptResult {
518    pub description: Option<String>,
519    pub messages: Vec<PromptMessage>,
520}
521
522/// Initialize request parameters
523#[derive(Debug, Clone, Serialize, Deserialize)]
524pub struct InitializeRequestParam {
525    #[serde(rename = "protocolVersion")]
526    pub protocol_version: String,
527    pub capabilities: serde_json::Value,
528    #[serde(rename = "clientInfo")]
529    pub client_info: Implementation,
530}
531
532/// Initialize result
533#[derive(Debug, Clone, Serialize, Deserialize)]
534pub struct InitializeResult {
535    #[serde(rename = "protocolVersion")]
536    pub protocol_version: String,
537    pub capabilities: ServerCapabilities,
538    #[serde(rename = "serverInfo")]
539    pub server_info: Implementation,
540    #[serde(skip_serializing_if = "Option::is_none")]
541    pub instructions: Option<String>,
542}
543
544/// Completion request parameters
545#[derive(Debug, Clone, Serialize, Deserialize)]
546pub struct CompleteRequestParam {
547    pub ref_: String,
548    pub argument: serde_json::Value,
549}
550
551/// Completion information
552#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct CompletionInfo {
554    pub completion: String,
555    pub has_more: Option<bool>,
556}
557
558/// Complete result
559#[derive(Debug, Clone, Serialize, Deserialize)]
560pub struct CompleteResult {
561    pub completion: Vec<CompletionInfo>,
562}
563
564/// Set logging level parameters
565#[derive(Debug, Clone, Serialize, Deserialize)]
566pub struct SetLevelRequestParam {
567    pub level: String,
568}
569
570/// Resource template definition
571#[derive(Debug, Clone, Serialize, Deserialize)]
572pub struct ResourceTemplate {
573    #[serde(rename = "uriTemplate")]
574    pub uri_template: String,
575    pub name: String,
576    pub description: Option<String>,
577    #[serde(rename = "mimeType")]
578    pub mime_type: Option<String>,
579}
580
581/// List resource templates result
582#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct ListResourceTemplatesResult {
584    #[serde(rename = "resourceTemplates")]
585    pub resource_templates: Vec<ResourceTemplate>,
586    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
587    pub next_cursor: Option<String>,
588}
589
590/// Subscribe request parameters
591#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct SubscribeRequestParam {
593    pub uri: String,
594}
595
596/// Unsubscribe request parameters
597#[derive(Debug, Clone, Serialize, Deserialize)]
598pub struct UnsubscribeRequestParam {
599    pub uri: String,
600}
601
602/// Elicitation request parameters
603#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct ElicitationRequestParam {
605    pub message: String,
606    #[serde(rename = "requestedSchema")]
607    pub requested_schema: serde_json::Value,
608}
609
610/// Elicitation response actions
611#[derive(Debug, Clone, Serialize, Deserialize)]
612#[serde(rename_all = "lowercase")]
613pub enum ElicitationAction {
614    Accept,
615    Decline,
616    Cancel,
617}
618
619/// Elicitation response
620#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct ElicitationResponse {
622    pub action: ElicitationAction,
623    #[serde(skip_serializing_if = "Option::is_none")]
624    pub data: Option<serde_json::Value>,
625}
626
627/// Elicitation result
628#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct ElicitationResult {
630    pub response: ElicitationResponse,
631}
632
633impl ElicitationResult {
634    /// Create an accept result with data
635    pub fn accept(data: serde_json::Value) -> Self {
636        Self {
637            response: ElicitationResponse {
638                action: ElicitationAction::Accept,
639                data: Some(data),
640            },
641        }
642    }
643
644    /// Create a decline result
645    pub fn decline() -> Self {
646        Self {
647            response: ElicitationResponse {
648                action: ElicitationAction::Decline,
649                data: None,
650            },
651        }
652    }
653
654    /// Create a cancel result
655    pub fn cancel() -> Self {
656        Self {
657            response: ElicitationResponse {
658                action: ElicitationAction::Cancel,
659                data: None,
660            },
661        }
662    }
663}