1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4pub const PROTOCOL_VERSIONS: &[&str] = &["2025-06-18", "2025-03-26", "2024-11-05"];
19
20pub const PROTOCOL_VERSION: &str = PROTOCOL_VERSIONS[0];
23
24pub fn negotiate_version(requested: Option<&str>) -> &'static str {
32 match requested {
33 Some(want) => PROTOCOL_VERSIONS
34 .iter()
35 .find(|v| **v == want)
36 .copied()
37 .unwrap_or(PROTOCOL_VERSION),
38 None => PROTOCOL_VERSION,
39 }
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct MCPRequest {
45 pub jsonrpc: String,
46 pub id: Option<Value>,
47 pub method: String,
48 pub params: Option<Value>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct MCPResponse {
54 pub jsonrpc: String,
55 pub id: Option<Value>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub result: Option<Value>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub error: Option<MCPError>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct MCPError {
65 pub code: i64,
66 pub message: String,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub data: Option<Value>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct InitializeParams {
74 pub protocol_version: String,
75 pub capabilities: ClientCapabilities,
76 pub client_info: ClientInfo,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct ClientCapabilities {
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub experimental: Option<Value>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub sampling: Option<Value>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct ClientInfo {
91 pub name: String,
92 pub version: String,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct ServerCapabilities {
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub tools: Option<Value>,
100 #[serde(skip_serializing_if = "Option::is_none")]
101 pub resources: Option<Value>,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub prompts: Option<Value>,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct ToolDefinition {
109 pub name: String,
110 pub description: String,
111 #[serde(rename = "inputSchema")]
112 pub input_schema: Value,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct ToolCallParams {
118 pub name: String,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub arguments: Option<Value>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(tag = "type")]
126pub enum Content {
127 #[serde(rename = "text")]
128 Text { text: String },
129 #[serde(rename = "image")]
130 Image {
131 data: String,
132 #[serde(rename = "mimeType")]
133 mime_type: String,
134 },
135 #[serde(rename = "resource")]
136 Resource {
137 uri: String,
138 #[serde(rename = "mimeType")]
139 mime_type: Option<String>,
140 text: Option<String>,
141 },
142}
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn echoes_a_revision_we_speak() {
149 for want in PROTOCOL_VERSIONS {
154 assert_eq!(negotiate_version(Some(want)), *want);
155 }
156 }
157
158 #[test]
159 fn answers_our_newest_when_we_cannot_honour_the_request() {
160 assert_eq!(negotiate_version(Some("1999-01-01")), PROTOCOL_VERSION);
163 assert_eq!(negotiate_version(None), PROTOCOL_VERSION);
164 assert_eq!(PROTOCOL_VERSION, "2025-06-18");
165 }
166
167 #[test]
168 fn the_oldest_revision_is_still_served() {
169 assert_eq!(negotiate_version(Some("2024-11-05")), "2024-11-05");
172 }
173}