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: 2024,
53            minor: 11,
54            patch: 5,
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    pub tools: Option<ToolsCapability>,
76    pub resources: Option<ResourcesCapability>,
77    pub prompts: Option<PromptsCapability>,
78    pub logging: Option<LoggingCapability>,
79    pub sampling: Option<SamplingCapability>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, Default)]
83pub struct ToolsCapability {
84    pub list_changed: Option<bool>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, Default)]
88pub struct ResourcesCapability {
89    pub subscribe: Option<bool>,
90    pub list_changed: Option<bool>,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, Default)]
94pub struct PromptsCapability {
95    pub list_changed: Option<bool>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, Default)]
99pub struct LoggingCapability {
100    pub level: Option<String>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, Default)]
104pub struct SamplingCapability {}
105
106impl ServerCapabilities {
107    pub fn builder() -> ServerCapabilitiesBuilder {
108        ServerCapabilitiesBuilder::default()
109    }
110}
111
112#[derive(Default)]
113pub struct ServerCapabilitiesBuilder {
114    capabilities: ServerCapabilities,
115}
116
117impl ServerCapabilitiesBuilder {
118    pub fn enable_tools(mut self) -> Self {
119        self.capabilities.tools = Some(ToolsCapability {
120            list_changed: Some(true),
121        });
122        self
123    }
124
125    pub fn enable_resources(mut self) -> Self {
126        self.capabilities.resources = Some(ResourcesCapability {
127            subscribe: Some(true),
128            list_changed: Some(true),
129        });
130        self
131    }
132
133    pub fn enable_prompts(mut self) -> Self {
134        self.capabilities.prompts = Some(PromptsCapability {
135            list_changed: Some(true),
136        });
137        self
138    }
139
140    pub fn enable_logging(mut self) -> Self {
141        self.capabilities.logging = Some(LoggingCapability {
142            level: Some("info".to_string()),
143        });
144        self
145    }
146
147    pub fn enable_sampling(mut self) -> Self {
148        self.capabilities.sampling = Some(SamplingCapability {});
149        self
150    }
151
152    pub fn build(self) -> ServerCapabilities {
153        self.capabilities
154    }
155}
156
157/// Server information response
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct ServerInfo {
160    pub protocol_version: ProtocolVersion,
161    pub capabilities: ServerCapabilities,
162    pub server_info: Implementation,
163    pub instructions: Option<String>,
164}
165
166/// Tool definition
167#[derive(Debug, Clone, Serialize, Deserialize)]
168#[serde(rename_all = "camelCase")]
169pub struct Tool {
170    pub name: String,
171    pub description: String,
172    pub input_schema: serde_json::Value,
173}
174
175/// List tools result
176#[derive(Debug, Clone, Serialize, Deserialize)]
177#[serde(rename_all = "camelCase")]
178pub struct ListToolsResult {
179    pub tools: Vec<Tool>,
180    pub next_cursor: String, // Changed from Option<String> to String
181}
182
183/// Pagination parameters
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct PaginatedRequestParam {
186    pub cursor: Option<String>,
187}
188
189/// Tool call parameters
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct CallToolRequestParam {
192    pub name: String,
193    pub arguments: Option<serde_json::Value>,
194}
195
196/// Content types for tool responses
197#[derive(Debug, Clone, Serialize, Deserialize)]
198#[serde(tag = "type")]
199pub enum Content {
200    #[serde(rename = "text")]
201    Text { text: String },
202    #[serde(rename = "image")]
203    Image { data: String, mime_type: String },
204    #[serde(rename = "resource")]
205    Resource {
206        resource: String,
207        text: Option<String>,
208    },
209}
210
211impl Content {
212    pub fn text(text: impl Into<String>) -> Self {
213        Self::Text { text: text.into() }
214    }
215
216    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
217        Self::Image {
218            data: data.into(),
219            mime_type: mime_type.into(),
220        }
221    }
222
223    pub fn resource(resource: impl Into<String>, text: Option<String>) -> Self {
224        Self::Resource {
225            resource: resource.into(),
226            text,
227        }
228    }
229
230    /// Get text content if this is a text content type
231    pub fn as_text(&self) -> Option<&Self> {
232        match self {
233            Self::Text { .. } => Some(self),
234            _ => None,
235        }
236    }
237}
238
239/// Text content struct for compatibility
240pub struct TextContent {
241    pub text: String,
242}
243
244impl Content {
245    /// Get text content as TextContent struct for compatibility
246    pub fn as_text_content(&self) -> Option<TextContent> {
247        match self {
248            Self::Text { text } => Some(TextContent { text: text.clone() }),
249            _ => None,
250        }
251    }
252}
253
254/// Tool call result
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct CallToolResult {
257    pub content: Vec<Content>,
258    pub is_error: Option<bool>,
259}
260
261impl CallToolResult {
262    pub fn success(content: Vec<Content>) -> Self {
263        Self {
264            content,
265            is_error: Some(false),
266        }
267    }
268
269    pub fn error(content: Vec<Content>) -> Self {
270        Self {
271            content,
272            is_error: Some(true),
273        }
274    }
275
276    pub fn text(text: impl Into<String>) -> Self {
277        Self::success(vec![Content::text(text)])
278    }
279
280    pub fn error_text(text: impl Into<String>) -> Self {
281        Self::error(vec![Content::text(text)])
282    }
283}
284
285/// Resource definition
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct Resource {
288    pub uri: String,
289    pub name: String,
290    pub description: Option<String>,
291    pub mime_type: Option<String>,
292    pub annotations: Option<Annotations>,
293    #[serde(skip_serializing_if = "Option::is_none")]
294    pub raw: Option<RawResource>,
295}
296
297/// Resource annotations
298#[derive(Debug, Clone, Serialize, Deserialize, Default)]
299pub struct Annotations {
300    pub audience: Option<Vec<String>>,
301    pub priority: Option<f32>,
302}
303
304/// List resources result
305#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct ListResourcesResult {
307    pub resources: Vec<Resource>,
308    pub next_cursor: Option<String>,
309}
310
311/// Read resource parameters
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct ReadResourceRequestParam {
314    pub uri: String,
315}
316
317/// Resource contents wrapper
318#[derive(Debug, Clone, Serialize, Deserialize)]
319pub struct ResourceContents {
320    pub uri: String,
321    pub mime_type: Option<String>,
322    pub text: Option<String>,
323    pub blob: Option<String>,
324}
325
326/// Read resource result
327#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct ReadResourceResult {
329    pub contents: Vec<ResourceContents>,
330}
331
332/// Raw resource (for internal use)
333#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct RawResource {
335    pub uri: String,
336    pub data: Vec<u8>,
337    pub mime_type: Option<String>,
338    pub name: Option<String>,
339    pub description: Option<String>,
340    pub size: Option<usize>,
341}
342
343impl PromptMessage {
344    /// Create a new text message
345    pub fn new_text(role: PromptMessageRole, text: impl Into<String>) -> Self {
346        Self {
347            role,
348            content: PromptMessageContent::Text { text: text.into() },
349        }
350    }
351
352    /// Create a new image message
353    pub fn new_image(
354        role: PromptMessageRole,
355        data: impl Into<String>,
356        mime_type: impl Into<String>,
357    ) -> Self {
358        Self {
359            role,
360            content: PromptMessageContent::Image {
361                data: data.into(),
362                mime_type: mime_type.into(),
363            },
364        }
365    }
366}
367
368impl CompleteResult {
369    /// Create a simple completion result
370    pub fn simple(completion: impl Into<String>) -> Self {
371        Self {
372            completion: vec![CompletionInfo {
373                completion: completion.into(),
374                has_more: Some(false),
375            }],
376        }
377    }
378}
379
380/// Prompt definition
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct Prompt {
383    pub name: String,
384    pub description: Option<String>,
385    pub arguments: Option<Vec<PromptArgument>>,
386}
387
388/// Prompt argument definition
389#[derive(Debug, Clone, Serialize, Deserialize)]
390pub struct PromptArgument {
391    pub name: String,
392    pub description: Option<String>,
393    pub required: Option<bool>,
394}
395
396/// List prompts result
397#[derive(Debug, Clone, Serialize, Deserialize)]
398pub struct ListPromptsResult {
399    pub prompts: Vec<Prompt>,
400    pub next_cursor: Option<String>,
401}
402
403/// Get prompt parameters
404#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct GetPromptRequestParam {
406    pub name: String,
407    pub arguments: Option<HashMap<String, String>>,
408}
409
410/// Prompt message role
411#[derive(Debug, Clone, Serialize, Deserialize)]
412#[serde(rename_all = "lowercase")]
413pub enum PromptMessageRole {
414    User,
415    Assistant,
416    System,
417}
418
419/// Prompt message content
420#[derive(Debug, Clone, Serialize, Deserialize)]
421#[serde(tag = "type")]
422pub enum PromptMessageContent {
423    #[serde(rename = "text")]
424    Text { text: String },
425    #[serde(rename = "image")]
426    Image { data: String, mime_type: String },
427}
428
429/// Prompt message
430#[derive(Debug, Clone, Serialize, Deserialize)]
431pub struct PromptMessage {
432    pub role: PromptMessageRole,
433    pub content: PromptMessageContent,
434}
435
436/// Get prompt result
437#[derive(Debug, Clone, Serialize, Deserialize)]
438pub struct GetPromptResult {
439    pub description: Option<String>,
440    pub messages: Vec<PromptMessage>,
441}
442
443/// Initialize request parameters
444#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct InitializeRequestParam {
446    #[serde(rename = "protocolVersion")]
447    pub protocol_version: String,
448    pub capabilities: serde_json::Value,
449    #[serde(rename = "clientInfo")]
450    pub client_info: Implementation,
451}
452
453/// Initialize result
454#[derive(Debug, Clone, Serialize, Deserialize)]
455pub struct InitializeResult {
456    #[serde(rename = "protocolVersion")]
457    pub protocol_version: String,
458    pub capabilities: ServerCapabilities,
459    #[serde(rename = "serverInfo")]
460    pub server_info: Implementation,
461    pub instructions: Option<String>,
462}
463
464/// Completion request parameters
465#[derive(Debug, Clone, Serialize, Deserialize)]
466pub struct CompleteRequestParam {
467    pub ref_: String,
468    pub argument: serde_json::Value,
469}
470
471/// Completion information
472#[derive(Debug, Clone, Serialize, Deserialize)]
473pub struct CompletionInfo {
474    pub completion: String,
475    pub has_more: Option<bool>,
476}
477
478/// Complete result
479#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct CompleteResult {
481    pub completion: Vec<CompletionInfo>,
482}
483
484/// Set logging level parameters
485#[derive(Debug, Clone, Serialize, Deserialize)]
486pub struct SetLevelRequestParam {
487    pub level: String,
488}
489
490/// Resource template definition
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct ResourceTemplate {
493    #[serde(rename = "uriTemplate")]
494    pub uri_template: String,
495    pub name: String,
496    pub description: Option<String>,
497    #[serde(rename = "mimeType")]
498    pub mime_type: Option<String>,
499}
500
501/// List resource templates result
502#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct ListResourceTemplatesResult {
504    #[serde(rename = "resourceTemplates")]
505    pub resource_templates: Vec<ResourceTemplate>,
506    #[serde(rename = "nextCursor")]
507    pub next_cursor: String, // Changed from Option<String> to String
508}
509
510/// Subscribe request parameters
511#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct SubscribeRequestParam {
513    pub uri: String,
514}
515
516/// Unsubscribe request parameters
517#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct UnsubscribeRequestParam {
519    pub uri: String,
520}