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 parse_version(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!(
40 self,
41 McpProtocolVersion::V2025_03_26 | McpProtocolVersion::V2025_06_18
42 )
43 }
44
45 pub fn supports_meta_fields(&self) -> bool {
47 matches!(self, McpProtocolVersion::V2025_06_18)
48 }
49
50 pub fn supports_progress_and_cursor(&self) -> bool {
52 matches!(self, McpProtocolVersion::V2025_06_18)
53 }
54
55 pub fn supports_elicitation(&self) -> bool {
57 matches!(self, McpProtocolVersion::V2025_06_18)
58 }
59
60 pub fn supported_features(&self) -> Vec<&'static str> {
62 let mut features = vec![];
63 if self.supports_streamable_http() {
64 features.push("streamable-http");
65 }
66 if self.supports_meta_fields() {
67 features.push("_meta-fields");
68 }
69 if self.supports_progress_and_cursor() {
70 features.push("progress-token");
71 features.push("cursor");
72 }
73 if self.supports_elicitation() {
74 features.push("elicitation");
75 }
76 features
77 }
78
79 pub const LATEST: McpProtocolVersion = McpProtocolVersion::V2025_06_18;
81}
82
83impl Default for McpProtocolVersion {
84 fn default() -> Self {
85 Self::LATEST
86 }
87}
88
89impl std::fmt::Display for McpProtocolVersion {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 write!(f, "{}", self.to_string())
92 }
93}
94
95pub fn extract_protocol_version(headers: &hyper::HeaderMap) -> McpProtocolVersion {
97 headers
98 .get("MCP-Protocol-Version")
99 .and_then(|h| h.to_str().ok())
100 .and_then(McpProtocolVersion::parse_version)
101 .unwrap_or(McpProtocolVersion::LATEST)
102}
103
104pub fn extract_session_id(headers: &hyper::HeaderMap) -> Option<String> {
106 headers
107 .get("Mcp-Session-Id")
108 .and_then(|h| h.to_str().ok())
109 .map(|s| s.to_string())
110}
111
112pub fn extract_last_event_id(headers: &hyper::HeaderMap) -> Option<u64> {
114 headers
115 .get("Last-Event-ID")
116 .and_then(|h| h.to_str().ok())
117 .and_then(|s| s.parse::<u64>().ok())
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use hyper::HeaderMap;
124
125 #[test]
126 fn test_version_parsing() {
127 assert_eq!(
128 McpProtocolVersion::parse_version("2024-11-05"),
129 Some(McpProtocolVersion::V2024_11_05)
130 );
131 assert_eq!(
132 McpProtocolVersion::parse_version("2025-03-26"),
133 Some(McpProtocolVersion::V2025_03_26)
134 );
135 assert_eq!(
136 McpProtocolVersion::parse_version("2025-06-18"),
137 Some(McpProtocolVersion::V2025_06_18)
138 );
139 assert_eq!(McpProtocolVersion::parse_version("invalid"), None);
140 }
141
142 #[test]
143 fn test_version_features() {
144 let v2024 = McpProtocolVersion::V2024_11_05;
145 assert!(!v2024.supports_streamable_http());
146 assert!(!v2024.supports_meta_fields());
147 assert!(!v2024.supports_elicitation());
148
149 let v2025_03 = McpProtocolVersion::V2025_03_26;
150 assert!(v2025_03.supports_streamable_http());
151 assert!(!v2025_03.supports_meta_fields());
152
153 let v2025_06 = McpProtocolVersion::V2025_06_18;
154 assert!(v2025_06.supports_streamable_http());
155 assert!(v2025_06.supports_meta_fields());
156 assert!(v2025_06.supports_elicitation());
157 }
158
159 #[test]
160 fn test_header_extraction() {
161 let mut headers = HeaderMap::new();
162 headers.insert("MCP-Protocol-Version", "2025-06-18".parse().unwrap());
163 headers.insert("Mcp-Session-Id", "test-session-123".parse().unwrap());
164
165 let version = extract_protocol_version(&headers);
166 assert_eq!(version, McpProtocolVersion::V2025_06_18);
167
168 let session_id = extract_session_id(&headers);
169 assert_eq!(session_id, Some("test-session-123".to_string()));
170 }
171}