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