mcp_probe_core/messages/
mod.rs

1//! MCP (Model Context Protocol) message types and JSON-RPC structures.
2//!
3//! This module provides complete type definitions for all MCP messages according to the
4//! MCP specification. All message types are designed to be:
5//!
6//! - **Spec-compliant**: Exact adherence to MCP JSON-RPC 2.0 protocol
7//! - **Type-safe**: Leverage Rust's type system to prevent protocol violations
8//! - **Serializable**: Full serde support for JSON serialization/deserialization
9//! - **Extensible**: Support for custom extensions and future protocol versions
10//! - **Debuggable**: Rich Debug implementations for development and debugging
11//!
12//! # Message Categories
13//!
14//! - **Core Messages**: Basic JSON-RPC request/response/notification structures
15//! - **Initialization**: Protocol version negotiation and capability discovery
16//! - **Tools**: Tool discovery, schema definition, and execution
17//! - **Resources**: Resource listing, reading, and subscription
18//! - **Prompts**: Prompt templates and completion requests
19//! - **Sampling**: LLM completion requests from server to client
20//! - **Logging**: Server-to-client logging messages
21//!
22//! # Examples
23//!
24//! ```rust
25//! use mcp_probe_core::messages::{JsonRpcRequest, InitializeRequest, ProtocolVersion, Implementation};
26//! use serde_json::json;
27//!
28//! // Create an initialization request
29//! let init_request = InitializeRequest {
30//!     protocol_version: ProtocolVersion::V2024_11_05,
31//!     capabilities: Default::default(),
32//!     client_info: Implementation {
33//!         name: "mcp-probe".to_string(),
34//!         version: "0.1.0".to_string(),
35//!         metadata: std::collections::HashMap::new(),
36//!     },
37//! };
38//!
39//! // Wrap in JSON-RPC request
40//! let request = JsonRpcRequest::new(
41//!     "1".to_string(),
42//!     "initialize".to_string(),
43//!     serde_json::to_value(init_request).unwrap(),
44//! );
45//! ```
46
47pub mod core;
48pub mod initialization;
49pub mod logging;
50pub mod prompts;
51pub mod resources;
52pub mod sampling;
53pub mod tools;
54
55pub use core::*;
56pub use initialization::*;
57pub use logging::{
58    LogLevel, LoggingNotification, ProgressNotification,
59    PromptListChangedNotification as LoggingPromptListChangedNotification,
60    ResourceListChangedNotification as LoggingResourceListChangedNotification,
61    ResourceUpdatedNotification as LoggingResourceUpdatedNotification, SetLevelRequest,
62    ToolListChangedNotification as LoggingToolListChangedNotification,
63};
64pub use prompts::{
65    GetPromptRequest, GetPromptResponse, ListPromptsRequest, ListPromptsResponse,
66    MessageRole as PromptMessageRole, Prompt, PromptContent, PromptListChangedNotification,
67    PromptMessage, ResourceReference as PromptResourceReference,
68};
69pub use resources::{
70    ListResourcesRequest, ListResourcesResponse, ReadResourceRequest, ReadResourceResponse,
71    Resource, ResourceContent, ResourceListChangedNotification, ResourceUpdatedNotification,
72    SubscribeRequest, UnsubscribeRequest,
73};
74pub use sampling::{
75    CompleteRequest, CompleteResponse, CompletionArgument, CompletionResult, CostPriority,
76    IntelligencePriority, MessageRole, ModelPreferences, SamplingContent, SamplingMessage,
77    SpeedPriority, StopReason,
78};
79pub use tools::{
80    CallToolRequest, CallToolResponse, ListToolsRequest, ListToolsResponse,
81    ResourceReference as ToolResourceReference, Tool, ToolListChangedNotification, ToolResult,
82};
83
84use serde::{Deserialize, Serialize};
85use std::collections::HashMap;
86
87/// MCP protocol version identifier.
88///
89/// The MCP protocol uses semantic versioning with date-based versions.
90/// This enum provides type-safe handling of supported protocol versions.
91#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
92pub enum ProtocolVersion {
93    /// MCP Protocol version 2024-11-05 (legacy)
94    #[serde(rename = "2024-11-05")]
95    V2024_11_05,
96
97    /// MCP Protocol version 2025-03-26 (current stable)
98    #[serde(rename = "2025-03-26")]
99    V2025_03_26,
100
101    /// Future protocol versions can be added here
102    /// Custom version string for forward compatibility
103    #[serde(untagged)]
104    Custom(String),
105}
106
107impl ProtocolVersion {
108    /// Get the string representation of the protocol version.
109    pub fn as_str(&self) -> &str {
110        match self {
111            Self::V2024_11_05 => "2024-11-05",
112            Self::V2025_03_26 => "2025-03-26",
113            Self::Custom(version) => version,
114        }
115    }
116
117    /// Check if this version is supported by the current implementation.
118    pub fn is_supported(&self) -> bool {
119        matches!(self, Self::V2024_11_05 | Self::V2025_03_26)
120    }
121
122    /// Get all supported protocol versions.
123    pub fn supported_versions() -> Vec<Self> {
124        vec![Self::V2024_11_05, Self::V2025_03_26]
125    }
126}
127
128impl Default for ProtocolVersion {
129    fn default() -> Self {
130        Self::V2025_03_26
131    }
132}
133
134impl std::fmt::Display for ProtocolVersion {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        write!(f, "{}", self.as_str())
137    }
138}
139
140/// Generic capability structure for extensible capability negotiation.
141///
142/// Capabilities are used during initialization to negotiate what features
143/// both client and server support. This structure allows for both standard
144/// and custom capabilities.
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
146pub struct Capabilities {
147    /// Standard MCP capabilities
148    #[serde(flatten)]
149    pub standard: StandardCapabilities,
150
151    /// Custom or experimental capabilities
152    #[serde(flatten)]
153    pub custom: HashMap<String, serde_json::Value>,
154}
155
156/// Standard MCP capabilities as defined in the specification.
157///
158/// These capabilities control what features are available during the MCP session.
159/// Both client and server declare their capabilities during initialization.
160#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
161pub struct StandardCapabilities {
162    /// Server capability: Can provide tools for execution
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub tools: Option<ToolCapabilities>,
165
166    /// Server capability: Can provide resources for reading
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub resources: Option<ResourceCapabilities>,
169
170    /// Server capability: Can provide prompt templates
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub prompts: Option<PromptCapabilities>,
173
174    /// Client capability: Can handle sampling requests from server
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub sampling: Option<SamplingCapabilities>,
177
178    /// Server capability: Can send log messages to client
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub logging: Option<LoggingCapabilities>,
181
182    /// Client capability: Can provide root directories for server operations
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub roots: Option<RootsCapabilities>,
185}
186
187/// Tool-related capabilities.
188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
189pub struct ToolCapabilities {
190    /// Whether the server supports listing changed tools
191    #[serde(skip_serializing_if = "Option::is_none")]
192    pub list_changed: Option<bool>,
193}
194
195/// Resource-related capabilities.
196#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
197pub struct ResourceCapabilities {
198    /// Whether the server supports subscribing to resource changes
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub subscribe: Option<bool>,
201
202    /// Whether the server supports listing changed resources
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub list_changed: Option<bool>,
205}
206
207/// Prompt-related capabilities.
208#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
209pub struct PromptCapabilities {
210    /// Whether the server supports listing changed prompts
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub list_changed: Option<bool>,
213}
214
215/// Sampling-related capabilities (client-side).
216#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
217pub struct SamplingCapabilities {
218    /// Whether the client supports receiving sampling requests
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub enabled: Option<bool>,
221}
222
223/// Logging-related capabilities.
224#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
225pub struct LoggingCapabilities {
226    /// Whether the server supports different log levels
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub level: Option<bool>,
229}
230
231/// Roots-related capabilities (client-side).
232#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
233pub struct RootsCapabilities {
234    /// Whether the client supports providing root directories
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub list_changed: Option<bool>,
237}
238
239/// Implementation information for client or server.
240///
241/// This provides metadata about the MCP implementation, useful for
242/// debugging, telemetry, and compatibility checking.
243#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
244pub struct Implementation {
245    /// Name of the implementation (e.g., "mcp-probe")
246    pub name: String,
247
248    /// Version of the implementation (e.g., "0.1.0")
249    pub version: String,
250
251    /// Additional implementation metadata
252    #[serde(flatten)]
253    pub metadata: HashMap<String, serde_json::Value>,
254}
255
256impl Implementation {
257    /// Create a new implementation info structure.
258    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
259        Self {
260            name: name.into(),
261            version: version.into(),
262            metadata: HashMap::new(),
263        }
264    }
265
266    /// Add custom metadata to the implementation info.
267    pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
268        self.metadata.insert(key.into(), value);
269        self
270    }
271}
272
273/// Progress token for long-running operations.
274///
275/// Operations that may take significant time can include progress tokens
276/// to allow clients to track progress and provide user feedback.
277#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
278#[serde(untagged)]
279pub enum ProgressToken {
280    /// String-based progress token
281    String(String),
282    /// Numeric progress token
283    Number(i64),
284}
285
286impl From<String> for ProgressToken {
287    fn from(s: String) -> Self {
288        Self::String(s)
289    }
290}
291
292impl From<&str> for ProgressToken {
293    fn from(s: &str) -> Self {
294        Self::String(s.to_string())
295    }
296}
297
298impl From<i64> for ProgressToken {
299    fn from(n: i64) -> Self {
300        Self::Number(n)
301    }
302}
303
304impl std::fmt::Display for ProgressToken {
305    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306        match self {
307            Self::String(s) => write!(f, "{}", s),
308            Self::Number(n) => write!(f, "{}", n),
309        }
310    }
311}
312
313/// Pagination cursor for list operations.
314///
315/// Used to support efficient pagination of large result sets in list operations.
316#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
317pub struct PaginationCursor {
318    /// Opaque cursor value for pagination
319    pub cursor: String,
320}
321
322impl PaginationCursor {
323    /// Create a new pagination cursor.
324    pub fn new(cursor: impl Into<String>) -> Self {
325        Self {
326            cursor: cursor.into(),
327        }
328    }
329}
330
331impl From<String> for PaginationCursor {
332    fn from(cursor: String) -> Self {
333        Self::new(cursor)
334    }
335}
336
337impl From<&str> for PaginationCursor {
338    fn from(cursor: &str) -> Self {
339        Self::new(cursor)
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346    use serde_json;
347
348    #[test]
349    fn test_protocol_version_serialization() {
350        let version = ProtocolVersion::V2024_11_05;
351        let json = serde_json::to_string(&version).unwrap();
352        assert_eq!(json, "\"2024-11-05\"");
353
354        let deserialized: ProtocolVersion = serde_json::from_str(&json).unwrap();
355        assert_eq!(deserialized, version);
356    }
357
358    #[test]
359    fn test_protocol_version_custom() {
360        let custom = ProtocolVersion::Custom("2025-01-01".to_string());
361        assert_eq!(custom.as_str(), "2025-01-01");
362        assert!(!custom.is_supported());
363    }
364
365    #[test]
366    fn test_capabilities_serialization() {
367        let capabilities = Capabilities {
368            standard: StandardCapabilities {
369                tools: Some(ToolCapabilities {
370                    list_changed: Some(true),
371                }),
372                resources: Some(ResourceCapabilities {
373                    subscribe: Some(true),
374                    list_changed: Some(false),
375                }),
376                ..Default::default()
377            },
378            custom: {
379                let mut custom = HashMap::new();
380                custom.insert("experimental".to_string(), serde_json::json!(true));
381                custom
382            },
383        };
384
385        let json = serde_json::to_value(&capabilities).unwrap();
386        let deserialized: Capabilities = serde_json::from_value(json).unwrap();
387        assert_eq!(deserialized, capabilities);
388    }
389
390    #[test]
391    fn test_implementation_creation() {
392        let impl_info = Implementation::new("mcp-probe", "0.1.0")
393            .with_metadata("platform", serde_json::json!("rust"));
394
395        assert_eq!(impl_info.name, "mcp-probe");
396        assert_eq!(impl_info.version, "0.1.0");
397        assert_eq!(
398            impl_info.metadata.get("platform").unwrap(),
399            &serde_json::json!("rust")
400        );
401    }
402
403    #[test]
404    fn test_progress_token_variants() {
405        let string_token = ProgressToken::from("progress-1");
406        let number_token = ProgressToken::from(42i64);
407
408        assert_eq!(string_token.to_string(), "progress-1");
409        assert_eq!(number_token.to_string(), "42");
410
411        // Test serialization
412        let json_string = serde_json::to_string(&string_token).unwrap();
413        let json_number = serde_json::to_string(&number_token).unwrap();
414
415        assert_eq!(json_string, "\"progress-1\"");
416        assert_eq!(json_number, "42");
417    }
418}