Skip to main content

intermcp/
protocol.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4pub const LATEST_PROTOCOL_VERSION: &str = "2024-11-05";
5pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &["2024-11-05"];
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(untagged)]
9pub enum Id {
10    Number(i64),
11    String(String),
12    Null,
13}
14
15impl Id {
16    pub fn to_string_key(&self) -> String {
17        match self {
18            Id::Number(n) => n.to_string(),
19            Id::String(s) => s.clone(),
20            Id::Null => "null".to_string(),
21        }
22    }
23
24    pub fn to_value(&self) -> Value {
25        match self {
26            Id::Number(n) => Value::Number((*n).into()),
27            Id::String(s) => Value::String(s.clone()),
28            Id::Null => Value::Null,
29        }
30    }
31}
32
33impl std::fmt::Display for Id {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        match self {
36            Id::Number(n) => write!(f, "{}", n),
37            Id::String(s) => write!(f, "{}", s),
38            Id::Null => write!(f, "null"),
39        }
40    }
41}
42
43impl From<Id> for Value {
44    fn from(id: Id) -> Self {
45        id.to_value()
46    }
47}
48
49impl From<&Id> for Value {
50    fn from(id: &Id) -> Self {
51        id.to_value()
52    }
53}
54
55impl From<i64> for Id {
56    fn from(n: i64) -> Self {
57        Id::Number(n)
58    }
59}
60
61impl From<i32> for Id {
62    fn from(n: i32) -> Self {
63        Id::Number(n as i64)
64    }
65}
66
67impl From<u64> for Id {
68    fn from(n: u64) -> Self {
69        Id::Number(n as i64)
70    }
71}
72
73impl From<String> for Id {
74    fn from(s: String) -> Self {
75        Id::String(s)
76    }
77}
78
79impl From<&str> for Id {
80    fn from(s: &str) -> Self {
81        Id::String(s.to_string())
82    }
83}
84
85impl From<Value> for Id {
86    fn from(v: Value) -> Self {
87        match v {
88            Value::Number(n) => {
89                if let Some(i) = n.as_i64() {
90                    Id::Number(i)
91                } else if let Some(u) = n.as_u64() {
92                    Id::Number(u as i64)
93                } else {
94                    Id::String(n.to_string())
95                }
96            }
97            Value::String(s) => Id::String(s),
98            _ => Id::Null,
99        }
100    }
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct JsonRpcRequest {
105    pub jsonrpc: String,
106    #[serde(default)]
107    pub id: Option<Id>,
108    pub method: String,
109    #[serde(default)]
110    pub params: Option<Value>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct JsonRpcResponse {
115    pub jsonrpc: String,
116    pub id: Value,
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub result: Option<Value>,
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub error: Option<JsonRpcError>,
121}
122
123impl JsonRpcResponse {
124    pub fn success(id: impl Into<Value>, result: Value) -> Self {
125        Self {
126            jsonrpc: "2.0".to_string(),
127            id: id.into(),
128            result: Some(result),
129            error: None,
130        }
131    }
132
133    pub fn error(id: impl Into<Value>, code: i32, message: String, data: Option<Value>) -> Self {
134        Self {
135            jsonrpc: "2.0".to_string(),
136            id: id.into(),
137            result: None,
138            error: Some(JsonRpcError {
139                code,
140                message,
141                data,
142            }),
143        }
144    }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct JsonRpcError {
149    pub code: i32,
150    pub message: String,
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub data: Option<Value>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase")]
157pub struct Implementation {
158    pub name: String,
159    pub version: String,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, Default)]
163#[serde(rename_all = "camelCase")]
164pub struct CapabilityInfo {
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub list_changed: Option<bool>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, Default)]
170#[serde(rename_all = "camelCase")]
171pub struct ServerCapabilities {
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub tools: Option<CapabilityInfo>,
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub resources: Option<CapabilityInfo>,
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub prompts: Option<CapabilityInfo>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181#[serde(rename_all = "camelCase")]
182pub struct InitializeResult {
183    pub protocol_version: String,
184    pub capabilities: ServerCapabilities,
185    pub server_info: Implementation,
186}
187
188// --- Tools ---
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
191#[serde(rename_all = "camelCase")]
192pub struct ToolDefinition {
193    pub name: String,
194    pub description: String,
195    pub input_schema: Value,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct ListToolsResult {
201    pub tools: Vec<ToolDefinition>,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
205#[serde(tag = "type", rename_all = "camelCase")]
206pub enum ContentItem {
207    #[serde(rename = "text")]
208    Text { text: String },
209    #[serde(rename = "image")]
210    Image { data: String, mime_type: String },
211    #[serde(rename = "resource")]
212    Resource { resource: ResourceContent },
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216#[serde(rename_all = "camelCase")]
217pub struct CallToolResult {
218    pub content: Vec<ContentItem>,
219    pub is_error: bool,
220}
221
222impl CallToolResult {
223    pub fn text(text: impl Into<String>) -> Self {
224        Self {
225            content: vec![ContentItem::Text { text: text.into() }],
226            is_error: false,
227        }
228    }
229
230    pub fn error(message: impl Into<String>) -> Self {
231        Self {
232            content: vec![ContentItem::Text {
233                text: message.into(),
234            }],
235            is_error: true,
236        }
237    }
238}
239
240// --- Resources ---
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct ResourceDefinition {
245    pub uri: String,
246    pub name: String,
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub description: Option<String>,
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub mime_type: Option<String>,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
254#[serde(rename_all = "camelCase")]
255pub struct ListResourcesResult {
256    pub resources: Vec<ResourceDefinition>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase")]
261pub struct ResourceContent {
262    pub uri: String,
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub mime_type: Option<String>,
265    pub text: String,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
270pub struct ReadResourceResult {
271    pub contents: Vec<ResourceContent>,
272}
273
274// --- Prompts ---
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
277#[serde(rename_all = "camelCase")]
278pub struct PromptArgument {
279    pub name: String,
280    #[serde(skip_serializing_if = "Option::is_none")]
281    pub description: Option<String>,
282    pub required: bool,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
286#[serde(rename_all = "camelCase")]
287pub struct PromptDefinition {
288    pub name: String,
289    pub description: String,
290    #[serde(skip_serializing_if = "Option::is_none")]
291    pub arguments: Option<Vec<PromptArgument>>,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
295#[serde(rename_all = "camelCase")]
296pub struct ListPromptsResult {
297    pub prompts: Vec<PromptDefinition>,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
301#[serde(rename_all = "camelCase")]
302pub struct PromptMessage {
303    pub role: String,
304    pub content: ContentItem,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
308#[serde(rename_all = "camelCase")]
309pub struct GetPromptResult {
310    pub description: Option<String>,
311    pub messages: Vec<PromptMessage>,
312}