1use std::borrow::Cow;
8
9use serde::{Deserialize, Serialize};
10use serde_json::value::RawValue;
11
12use crate::error::McpError;
13use crate::limits::Limits;
14use crate::spec::{JsonRpcVersion, RequestId};
15
16#[derive(Debug)]
18pub enum Message {
19 Request(Request),
21 Notification(Notification),
23}
24
25#[derive(Debug)]
27pub struct Request {
28 pub id: RequestId,
30 pub method: String,
32 pub params: Option<Box<RawValue>>,
34}
35
36#[derive(Debug)]
38pub struct Notification {
39 pub method: String,
41 pub params: Option<Box<RawValue>>,
43}
44
45#[derive(Debug)]
47pub enum Rejection {
48 Error {
50 id: Option<RequestId>,
52 error: McpError,
54 },
55 UnexpectedResponse,
58}
59
60#[derive(Deserialize)]
61struct Envelope<'a> {
62 #[serde(borrow, default)]
63 jsonrpc: Option<Cow<'a, str>>,
64 #[serde(borrow, default, deserialize_with = "present")]
67 id: Option<&'a RawValue>,
68 #[serde(borrow, default)]
69 method: Option<Cow<'a, str>>,
70 #[serde(borrow, default)]
71 params: Option<&'a RawValue>,
72 #[serde(borrow, default)]
73 result: Option<&'a RawValue>,
74 #[serde(borrow, default)]
75 error: Option<&'a RawValue>,
76}
77
78fn present<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Option<&'de RawValue>, D::Error> {
79 <&'de RawValue>::deserialize(d).map(Some)
80}
81
82pub fn parse(bytes: &[u8], limits: &Limits) -> Result<Message, Rejection> {
88 let reject = |error| Rejection::Error { id: None, error };
89
90 if bytes.len() > limits.max_message_bytes {
91 return Err(reject(McpError::invalid_request("Message too large")));
92 }
93 if exceeds_depth(bytes, limits.effective_json_depth()) {
94 return Err(reject(McpError::invalid_request(
95 "Message nested too deeply",
96 )));
97 }
98 if first_significant_byte(bytes) == Some(b'[') {
99 return Err(reject(McpError::invalid_request(
100 "Batch requests are not supported",
101 )));
102 }
103
104 let envelope: Envelope<'_> = serde_json::from_slice(bytes).map_err(|e| {
105 reject(if e.is_data() {
106 McpError::invalid_request("Invalid JSON-RPC message")
107 } else {
108 McpError::parse_error()
109 })
110 })?;
111
112 let id = match envelope.id {
113 None => None,
114 Some(raw) if raw.get() == "null" => {
115 return Err(reject(McpError::invalid_request(
116 "Request id must not be null",
117 )));
118 }
119 Some(raw) => Some(
120 serde_json::from_str::<RequestId>(raw.get())
121 .map_err(|_| reject(McpError::invalid_request("Invalid request id")))?,
122 ),
123 };
124 let reject_with_id = |error| Rejection::Error {
125 id: id.clone(),
126 error,
127 };
128
129 if envelope.jsonrpc.as_deref() != Some(JsonRpcVersion::VALUE) {
130 return Err(reject_with_id(McpError::invalid_request(
131 "Unsupported JSON-RPC version",
132 )));
133 }
134 if envelope.result.is_some() || envelope.error.is_some() {
135 return Err(Rejection::UnexpectedResponse);
136 }
137 let Some(method) = envelope.method else {
138 return Err(reject_with_id(McpError::invalid_request("Missing method")));
139 };
140 let method = method.into_owned();
141 let params = envelope.params.map(RawValue::to_owned);
142
143 Ok(match id {
144 Some(id) => Message::Request(Request { id, method, params }),
145 None => Message::Notification(Notification { method, params }),
146 })
147}
148
149#[cfg_attr(not(any(feature = "stdio", feature = "http")), allow(dead_code))]
151#[derive(Debug)]
152pub(crate) struct ClientResponse {
153 pub(crate) id: RequestId,
154 pub(crate) outcome: Result<serde_json::Value, serde_json::Value>,
156}
157
158#[cfg_attr(not(any(feature = "stdio", feature = "http")), allow(dead_code))]
159pub(crate) fn parse_client_response(bytes: &[u8]) -> Option<ClientResponse> {
162 #[derive(Deserialize)]
163 struct Wire {
164 id: RequestId,
165 #[serde(default)]
166 result: Option<serde_json::Value>,
167 #[serde(default)]
168 error: Option<serde_json::Value>,
169 }
170 let wire: Wire = serde_json::from_slice(bytes).ok()?;
171 let outcome = match (wire.result, wire.error) {
172 (_, Some(error)) => Err(error),
173 (Some(result), None) => Ok(result),
174 (None, None) => return None,
175 };
176 Some(ClientResponse {
177 id: wire.id,
178 outcome,
179 })
180}
181
182#[derive(Debug, Clone)]
184pub enum Response {
185 Result {
187 id: RequestId,
189 result: Box<RawValue>,
191 },
192 Error {
194 id: Option<RequestId>,
196 error: McpError,
198 },
199}
200
201#[derive(Serialize)]
202struct ResultWire<'a> {
203 jsonrpc: JsonRpcVersion,
204 id: &'a RequestId,
205 result: &'a RawValue,
206}
207
208#[derive(Serialize)]
209struct ErrorWire<'a> {
210 jsonrpc: JsonRpcVersion,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 id: Option<&'a RequestId>,
213 error: ErrorObjectWire<'a>,
214}
215
216#[derive(Serialize)]
217struct ErrorObjectWire<'a> {
218 code: i64,
219 message: &'a str,
220 #[serde(skip_serializing_if = "Option::is_none")]
221 data: Option<&'a serde_json::Value>,
222}
223
224impl Response {
225 #[must_use]
227 pub fn error(id: Option<RequestId>, error: McpError) -> Self {
228 Self::Error { id, error }
229 }
230
231 #[must_use]
233 pub fn as_error(&self) -> Option<&McpError> {
234 match self {
235 Self::Error { error, .. } => Some(error),
236 Self::Result { .. } => None,
237 }
238 }
239
240 #[must_use]
242 pub fn to_vec(&self) -> Vec<u8> {
243 let serialized = match self {
244 Self::Result { id, result } => serde_json::to_vec(&ResultWire {
245 jsonrpc: JsonRpcVersion::default(),
246 id,
247 result,
248 }),
249 Self::Error { id, error } => serde_json::to_vec(&ErrorWire {
250 jsonrpc: JsonRpcVersion::default(),
251 id: id.as_ref(),
252 error: ErrorObjectWire {
253 code: error.code(),
254 message: error.message(),
255 data: error.data(),
256 },
257 }),
258 };
259 serialized.unwrap_or_else(|_| {
262 br#"{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error"}}"#.to_vec()
263 })
264 }
265}
266
267pub(crate) fn exceeds_depth(bytes: &[u8], max: usize) -> bool {
273 let mut depth = 0usize;
274 let mut i = 0;
275 while let Some(&b) = bytes.get(i) {
276 i += 1;
277 match b {
278 b'"' => {
279 loop {
281 let rest = bytes.get(i..).unwrap_or_default();
282 let Some(offset) = memchr::memchr2(b'"', b'\\', rest) else {
283 return false;
285 };
286 let found = rest.get(offset).copied();
287 i += offset + 1;
288 if found == Some(b'\\') {
289 i += 1;
291 } else {
292 break;
293 }
294 }
295 }
296 b'{' | b'[' => {
297 depth += 1;
298 if depth > max {
299 return true;
300 }
301 }
302 b'}' | b']' => depth = depth.saturating_sub(1),
303 _ => {}
304 }
305 }
306 false
307}
308
309fn first_significant_byte(bytes: &[u8]) -> Option<u8> {
310 bytes.iter().copied().find(|b| !b.is_ascii_whitespace())
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 fn limits() -> Limits {
318 Limits::default()
319 }
320
321 fn rejection_code(r: Result<Message, Rejection>) -> i64 {
322 match r {
323 Err(Rejection::Error { error, .. }) => error.code(),
324 other => panic!("expected an error rejection, got {other:?}"),
325 }
326 }
327
328 #[test]
329 fn parses_request_and_notification() {
330 let req = parse(
331 br#"{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"a":1}}"#,
332 &limits(),
333 );
334 match req {
335 Ok(Message::Request(r)) => {
336 assert_eq!(r.id, RequestId::Integer(1));
337 assert_eq!(r.method, "tools/list");
338 assert_eq!(
339 r.params.map(|p| p.get().to_owned()).as_deref(),
340 Some(r#"{"a":1}"#)
341 );
342 }
343 other => panic!("unexpected {other:?}"),
344 }
345 let note = parse(
346 br#"{"jsonrpc":"2.0","method":"notifications/cancelled"}"#,
347 &limits(),
348 );
349 assert!(matches!(note, Ok(Message::Notification(_))));
350 }
351
352 #[test]
353 fn rejects_invalid_messages() {
354 assert_eq!(rejection_code(parse(b"{not json", &limits())), -32700);
355 assert_eq!(rejection_code(parse(b"[]", &limits())), -32600);
356 assert_eq!(
357 rejection_code(parse(
358 br#"{"jsonrpc":"2.0","id":null,"method":"x"}"#,
359 &limits()
360 )),
361 -32600
362 );
363 assert_eq!(
364 rejection_code(parse(
365 br#"{"jsonrpc":"1.0","id":1,"method":"x"}"#,
366 &limits()
367 )),
368 -32600
369 );
370 assert_eq!(
371 rejection_code(parse(br#"{"jsonrpc":"2.0","id":1}"#, &limits())),
372 -32600
373 );
374 assert_eq!(
375 rejection_code(parse(
376 br#"{"jsonrpc":"2.0","id":1.5,"method":"x"}"#,
377 &limits()
378 )),
379 -32600
380 );
381 assert!(matches!(
382 parse(br#"{"jsonrpc":"2.0","id":1,"result":{}}"#, &limits()),
383 Err(Rejection::UnexpectedResponse)
384 ));
385 }
386
387 #[test]
388 fn enforces_size_and_depth() {
389 let small = Limits {
390 max_message_bytes: 10,
391 ..Limits::default()
392 };
393 assert_eq!(
394 rejection_code(parse(br#"{"jsonrpc":"2.0","method":"x"}"#, &small)),
395 -32600
396 );
397 let deep = format!(
398 r#"{{"jsonrpc":"2.0","method":"x","params":{}{}}}"#,
399 "[".repeat(100),
400 "]".repeat(100)
401 );
402 assert_eq!(rejection_code(parse(deep.as_bytes(), &limits())), -32600);
403 }
404
405 #[test]
406 fn depth_scan_ignores_strings() {
407 assert!(!exceeds_depth(br#"{"a":"[[[[[[[[[[\"[[["}"#, 2));
408 assert!(exceeds_depth(br#"{"a":[[1]]}"#, 2));
409 assert!(!exceeds_depth(br#"{"a":[1]}"#, 2));
410 assert!(exceeds_depth(br#"{"a":"x\\","b":[[1]]}"#, 2));
412 assert!(!exceeds_depth(br#"{"a":"\"[[[[","b":1}"#, 2));
414 assert!(!exceeds_depth(br#"{"a":"[[[["#, 2));
416 }
417
418 #[test]
419 fn serializes_responses() {
420 let ok = Response::Result {
421 id: RequestId::String("a".into()),
422 result: RawValue::from_string(r#"{"resultType":"complete"}"#.into()).unwrap(),
423 };
424 assert_eq!(
425 String::from_utf8(ok.to_vec()).unwrap(),
426 r#"{"jsonrpc":"2.0","id":"a","result":{"resultType":"complete"}}"#
427 );
428 let err = Response::error(None, McpError::parse_error());
429 assert_eq!(
430 String::from_utf8(err.to_vec()).unwrap(),
431 r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"}}"#
432 );
433 }
434}