turul_http_mcp_server/
protocol.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum McpProtocolVersion {
9 V2024_11_05,
11 V2025_03_26,
13 V2025_06_18,
15}
16
17impl McpProtocolVersion {
18 pub fn from_str(s: &str) -> Option<Self> {
20 match s {
21 "2024-11-05" => Some(McpProtocolVersion::V2024_11_05),
22 "2025-03-26" => Some(McpProtocolVersion::V2025_03_26),
23 "2025-06-18" => Some(McpProtocolVersion::V2025_06_18),
24 _ => None,
25 }
26 }
27
28 pub fn to_string(&self) -> &'static str {
30 match self {
31 McpProtocolVersion::V2024_11_05 => "2024-11-05",
32 McpProtocolVersion::V2025_03_26 => "2025-03-26",
33 McpProtocolVersion::V2025_06_18 => "2025-06-18",
34 }
35 }
36
37 pub fn supports_streamable_http(&self) -> bool {
39 matches!(self, McpProtocolVersion::V2025_03_26 | McpProtocolVersion::V2025_06_18)
40 }
41
42 pub fn supports_meta_fields(&self) -> bool {
44 matches!(self, McpProtocolVersion::V2025_06_18)
45 }
46
47 pub fn supports_progress_and_cursor(&self) -> bool {
49 matches!(self, McpProtocolVersion::V2025_06_18)
50 }
51
52 pub fn supports_elicitation(&self) -> bool {
54 matches!(self, McpProtocolVersion::V2025_06_18)
55 }
56
57 pub fn supported_features(&self) -> Vec<&'static str> {
59 let mut features = vec![];
60 if self.supports_streamable_http() {
61 features.push("streamable-http");
62 }
63 if self.supports_meta_fields() {
64 features.push("_meta-fields");
65 }
66 if self.supports_progress_and_cursor() {
67 features.push("progress-token");
68 features.push("cursor");
69 }
70 if self.supports_elicitation() {
71 features.push("elicitation");
72 }
73 features
74 }
75
76 pub const LATEST: McpProtocolVersion = McpProtocolVersion::V2025_06_18;
78}
79
80impl Default for McpProtocolVersion {
81 fn default() -> Self {
82 Self::LATEST
83 }
84}
85
86impl std::fmt::Display for McpProtocolVersion {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 write!(f, "{}", self.to_string())
89 }
90}
91
92pub fn extract_protocol_version(headers: &hyper::HeaderMap) -> McpProtocolVersion {
94 headers
95 .get("MCP-Protocol-Version")
96 .and_then(|h| h.to_str().ok())
97 .and_then(McpProtocolVersion::from_str)
98 .unwrap_or(McpProtocolVersion::LATEST)
99}
100
101pub fn extract_session_id(headers: &hyper::HeaderMap) -> Option<String> {
103 headers
104 .get("Mcp-Session-Id")
105 .and_then(|h| h.to_str().ok())
106 .map(|s| s.to_string())
107}
108
109pub fn extract_last_event_id(headers: &hyper::HeaderMap) -> Option<u64> {
111 headers
112 .get("Last-Event-ID")
113 .and_then(|h| h.to_str().ok())
114 .and_then(|s| s.parse::<u64>().ok())
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use hyper::HeaderMap;
121
122 #[test]
123 fn test_version_parsing() {
124 assert_eq!(McpProtocolVersion::from_str("2024-11-05"), Some(McpProtocolVersion::V2024_11_05));
125 assert_eq!(McpProtocolVersion::from_str("2025-03-26"), Some(McpProtocolVersion::V2025_03_26));
126 assert_eq!(McpProtocolVersion::from_str("2025-06-18"), Some(McpProtocolVersion::V2025_06_18));
127 assert_eq!(McpProtocolVersion::from_str("invalid"), None);
128 }
129
130 #[test]
131 fn test_version_features() {
132 let v2024 = McpProtocolVersion::V2024_11_05;
133 assert!(!v2024.supports_streamable_http());
134 assert!(!v2024.supports_meta_fields());
135 assert!(!v2024.supports_elicitation());
136
137 let v2025_03 = McpProtocolVersion::V2025_03_26;
138 assert!(v2025_03.supports_streamable_http());
139 assert!(!v2025_03.supports_meta_fields());
140
141 let v2025_06 = McpProtocolVersion::V2025_06_18;
142 assert!(v2025_06.supports_streamable_http());
143 assert!(v2025_06.supports_meta_fields());
144 assert!(v2025_06.supports_elicitation());
145 }
146
147 #[test]
148 fn test_header_extraction() {
149 let mut headers = HeaderMap::new();
150 headers.insert("MCP-Protocol-Version", "2025-06-18".parse().unwrap());
151 headers.insert("Mcp-Session-Id", "test-session-123".parse().unwrap());
152
153 let version = extract_protocol_version(&headers);
154 assert_eq!(version, McpProtocolVersion::V2025_06_18);
155
156 let session_id = extract_session_id(&headers);
157 assert_eq!(session_id, Some("test-session-123".to_string()));
158 }
159}