1use std::{marker::PhantomData, sync::Arc};
2
3use futures::{SinkExt, StreamExt};
5use serde::{Serialize, de::DeserializeOwned};
6use thiserror::Error;
7use tokio::{
8 io::{AsyncRead, AsyncWrite},
9 sync::Mutex,
10};
11use tokio_util::{
12 bytes::{Buf, BufMut, BytesMut},
13 codec::{Decoder, Encoder, FramedRead, FramedWrite},
14};
15
16use super::{IntoTransport, Transport};
17use crate::service::{RxJsonRpcMessage, ServiceRole, TxJsonRpcMessage};
18
19pub enum TransportAdapterAsyncRW {}
20
21impl<Role, R, W> IntoTransport<Role, std::io::Error, TransportAdapterAsyncRW> for (R, W)
22where
23 Role: ServiceRole,
24 R: AsyncRead + Send + 'static + Unpin,
25 W: AsyncWrite + Send + 'static + Unpin,
26{
27 fn into_transport(self) -> impl Transport<Role, Error = std::io::Error> + 'static {
28 AsyncRwTransport::new(self.0, self.1)
29 }
30}
31
32pub enum TransportAdapterAsyncCombinedRW {}
33impl<Role, S> IntoTransport<Role, std::io::Error, TransportAdapterAsyncCombinedRW> for S
34where
35 Role: ServiceRole,
36 S: AsyncRead + AsyncWrite + Send + 'static,
37{
38 fn into_transport(self) -> impl Transport<Role, Error = std::io::Error> + 'static {
39 IntoTransport::<Role, std::io::Error, TransportAdapterAsyncRW>::into_transport(
40 tokio::io::split(self),
41 )
42 }
43}
44
45pub type TransportWriter<Role, W> = FramedWrite<W, JsonRpcMessageCodec<TxJsonRpcMessage<Role>>>;
46
47pub struct AsyncRwTransport<Role: ServiceRole, R: AsyncRead, W: AsyncWrite> {
48 read: FramedRead<R, JsonRpcMessageCodec<RxJsonRpcMessage<Role>>>,
49 write: Arc<Mutex<Option<TransportWriter<Role, W>>>>,
50}
51
52impl<Role: ServiceRole, R, W> AsyncRwTransport<Role, R, W>
53where
54 R: Send + AsyncRead + Unpin,
55 W: Send + AsyncWrite + Unpin + 'static,
56{
57 pub fn new(read: R, write: W) -> Self {
58 let read = FramedRead::new(
59 read,
60 JsonRpcMessageCodec::<RxJsonRpcMessage<Role>>::default(),
61 );
62 let write = Arc::new(Mutex::new(Some(FramedWrite::new(
63 write,
64 JsonRpcMessageCodec::<TxJsonRpcMessage<Role>>::default(),
65 ))));
66 Self { read, write }
67 }
68}
69
70#[cfg(feature = "client")]
71impl<R, W> AsyncRwTransport<crate::RoleClient, R, W>
72where
73 R: Send + AsyncRead + Unpin,
74 W: Send + AsyncWrite + Unpin + 'static,
75{
76 pub fn new_client(read: R, write: W) -> Self {
77 Self::new(read, write)
78 }
79}
80
81#[cfg(feature = "server")]
82impl<R, W> AsyncRwTransport<crate::RoleServer, R, W>
83where
84 R: Send + AsyncRead + Unpin,
85 W: Send + AsyncWrite + Unpin + 'static,
86{
87 pub fn new_server(read: R, write: W) -> Self {
88 Self::new(read, write)
89 }
90}
91
92impl<Role: ServiceRole, R, W> Transport<Role> for AsyncRwTransport<Role, R, W>
93where
94 R: Send + AsyncRead + Unpin,
95 W: Send + AsyncWrite + Unpin + 'static,
96{
97 type Error = std::io::Error;
98
99 fn send(
100 &mut self,
101 item: TxJsonRpcMessage<Role>,
102 ) -> impl Future<Output = Result<(), Self::Error>> + Send + 'static {
103 let lock = self.write.clone();
104 async move {
105 let mut write = lock.lock().await;
106 if let Some(ref mut write) = *write {
107 write.send(item).await.map_err(Into::into)
108 } else {
109 Err(std::io::Error::new(
110 std::io::ErrorKind::NotConnected,
111 "Transport is closed",
112 ))
113 }
114 }
115 }
116
117 fn receive(&mut self) -> impl Future<Output = Option<RxJsonRpcMessage<Role>>> {
118 let next = self.read.next();
119 async {
120 next.await.and_then(|e| {
121 e.inspect_err(|e| {
122 tracing::error!("Error reading from stream: {}", e);
123 })
124 .ok()
125 })
126 }
127 }
128
129 async fn close(&mut self) -> Result<(), Self::Error> {
130 let mut write = self.write.lock().await;
131 drop(write.take());
132 Ok(())
133 }
134}
135
136#[derive(Debug, Clone)]
137pub struct JsonRpcMessageCodec<T> {
138 _marker: PhantomData<fn() -> T>,
139 next_index: usize,
140 max_length: usize,
141 is_discarding: bool,
142}
143
144impl<T> Default for JsonRpcMessageCodec<T> {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150impl<T> JsonRpcMessageCodec<T> {
151 pub fn new() -> Self {
152 Self {
153 _marker: PhantomData,
154 next_index: 0,
155 max_length: usize::MAX,
156 is_discarding: false,
157 }
158 }
159
160 pub fn new_with_max_length(max_length: usize) -> Self {
161 Self {
162 max_length,
163 ..Self::new()
164 }
165 }
166
167 pub fn max_length(&self) -> usize {
168 self.max_length
169 }
170}
171
172fn without_carriage_return(s: &[u8]) -> &[u8] {
173 if let Some(&b'\r') = s.last() {
174 &s[..s.len() - 1]
175 } else {
176 s
177 }
178}
179
180fn is_standard_method(method: &str) -> bool {
185 matches!(
186 method,
187 "initialize"
188 | "ping"
189 | "prompts/get"
190 | "prompts/list"
191 | "resources/list"
192 | "resources/read"
193 | "resources/subscribe"
194 | "resources/unsubscribe"
195 | "resources/templates/list"
196 | "tools/call"
197 | "tools/list"
198 | "completion/complete"
199 | "logging/setLevel"
200 | "roots/list"
201 | "sampling/createMessage"
202 ) || is_standard_notification(method)
203}
204
205fn is_standard_notification(method: &str) -> bool {
206 matches!(
207 method,
208 "notifications/cancelled"
209 | "notifications/initialized"
210 | "notifications/message"
211 | "notifications/progress"
212 | "notifications/prompts/list_changed"
213 | "notifications/resources/list_changed"
214 | "notifications/resources/updated"
215 | "notifications/roots/list_changed"
216 | "notifications/tools/list_changed"
217 )
218}
219
220fn should_ignore_notification(json_value: &serde_json::Value, method: &str) -> bool {
222 let is_notification = json_value.get("id").is_none();
223
224 if is_notification && !is_standard_method(method) {
226 tracing::trace!(
227 "Ignoring non-MCP notification '{}' for compatibility",
228 method
229 );
230 return true;
231 }
232
233 matches!(
235 (
236 method.starts_with("notifications/"),
237 is_standard_notification(method)
238 ),
239 (true, false)
240 )
241}
242
243fn try_parse_with_compatibility<T: serde::de::DeserializeOwned>(
245 line: &[u8],
246 context: &str,
247) -> Result<Option<T>, JsonRpcMessageCodecError> {
248 if let Ok(line_str) = std::str::from_utf8(line) {
249 match serde_json::from_slice(line) {
250 Ok(item) => Ok(Some(item)),
251 Err(e) => {
252 if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(line_str) {
254 if let Some(method) =
255 json_value.get("method").and_then(serde_json::Value::as_str)
256 {
257 if should_ignore_notification(&json_value, method) {
258 return Ok(None);
259 }
260 }
261 }
262
263 tracing::debug!(
264 "Failed to parse message {}: {} | Error: {}",
265 context,
266 line_str,
267 e
268 );
269 Err(JsonRpcMessageCodecError::Serde(e))
270 }
271 }
272 } else {
273 serde_json::from_slice(line)
274 .map(Some)
275 .map_err(JsonRpcMessageCodecError::Serde)
276 }
277}
278
279#[derive(Debug, Error)]
280pub enum JsonRpcMessageCodecError {
281 #[error("max line length exceeded")]
282 MaxLineLengthExceeded,
283 #[error("serde error {0}")]
284 Serde(#[from] serde_json::Error),
285 #[error("io error {0}")]
286 Io(#[from] std::io::Error),
287}
288
289impl From<JsonRpcMessageCodecError> for std::io::Error {
290 fn from(value: JsonRpcMessageCodecError) -> Self {
291 match value {
292 JsonRpcMessageCodecError::MaxLineLengthExceeded => {
293 std::io::Error::new(std::io::ErrorKind::InvalidData, value)
294 }
295 JsonRpcMessageCodecError::Serde(e) => e.into(),
296 JsonRpcMessageCodecError::Io(e) => e,
297 }
298 }
299}
300
301impl<T: DeserializeOwned> Decoder for JsonRpcMessageCodec<T> {
302 type Item = T;
303
304 type Error = JsonRpcMessageCodecError;
305
306 fn decode(
307 &mut self,
308 buf: &mut BytesMut,
309 ) -> Result<Option<Self::Item>, JsonRpcMessageCodecError> {
310 loop {
311 let read_to = std::cmp::min(self.max_length.saturating_add(1), buf.len());
314
315 let newline_offset = buf[self.next_index..read_to]
316 .iter()
317 .position(|b| *b == b'\n');
318
319 match (self.is_discarding, newline_offset) {
320 (true, Some(offset)) => {
321 buf.advance(offset + self.next_index + 1);
325 self.is_discarding = false;
326 self.next_index = 0;
327 }
328 (true, None) => {
329 buf.advance(read_to);
333 self.next_index = 0;
334 if buf.is_empty() {
335 return Ok(None);
336 }
337 }
338 (false, Some(offset)) => {
339 let newline_index = offset + self.next_index;
341 self.next_index = 0;
342 let line = buf.split_to(newline_index + 1);
343 let line = &line[..line.len() - 1];
344 let line = without_carriage_return(line);
345
346 let item = match try_parse_with_compatibility(line, "decode")? {
348 Some(item) => item,
349 None => return Ok(None), };
351 return Ok(Some(item));
352 }
353 (false, None) if buf.len() > self.max_length => {
354 self.is_discarding = true;
358 return Err(JsonRpcMessageCodecError::MaxLineLengthExceeded);
359 }
360 (false, None) => {
361 self.next_index = read_to;
364 return Ok(None);
365 }
366 }
367 }
368 }
369
370 fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<T>, JsonRpcMessageCodecError> {
371 Ok(match self.decode(buf)? {
372 Some(frame) => Some(frame),
373 None => {
374 self.next_index = 0;
375 if buf.is_empty() || buf == &b"\r"[..] {
377 None
378 } else {
379 let line = buf.split_to(buf.len());
380 let line = without_carriage_return(&line);
381
382 let item = match try_parse_with_compatibility(line, "decode_eof")? {
384 Some(item) => item,
385 None => return Ok(None), };
387 Some(item)
388 }
389 }
390 })
391 }
392}
393
394impl<T: Serialize> Encoder<T> for JsonRpcMessageCodec<T> {
395 type Error = JsonRpcMessageCodecError;
396
397 fn encode(&mut self, item: T, buf: &mut BytesMut) -> Result<(), JsonRpcMessageCodecError> {
398 serde_json::to_writer(buf.writer(), &item)?;
399 buf.put_u8(b'\n');
400 Ok(())
401 }
402}
403
404#[cfg(test)]
405mod test {
406 use futures::{Sink, Stream};
407
408 use super::*;
409 fn from_async_read<T: DeserializeOwned, R: AsyncRead>(reader: R) -> impl Stream<Item = T> {
410 FramedRead::new(reader, JsonRpcMessageCodec::<T>::default()).filter_map(|result| {
411 if let Err(e) = &result {
412 tracing::error!("Error reading from stream: {}", e);
413 }
414 futures::future::ready(result.ok())
415 })
416 }
417
418 fn from_async_write<T: Serialize, W: AsyncWrite + Send>(
419 writer: W,
420 ) -> impl Sink<T, Error = std::io::Error> {
421 FramedWrite::new(writer, JsonRpcMessageCodec::<T>::default()).sink_map_err(Into::into)
422 }
423 #[tokio::test]
424 async fn test_decode() {
425 use futures::StreamExt;
426 use tokio::io::BufReader;
427
428 let data = r#"{"jsonrpc":"2.0","method":"subtract","params":[42,23],"id":1}
429 {"jsonrpc":"2.0","method":"subtract","params":[23,42],"id":2}
430 {"jsonrpc":"2.0","method":"subtract","params":[42,23],"id":3}
431 {"jsonrpc":"2.0","method":"subtract","params":[23,42],"id":4}
432 {"jsonrpc":"2.0","method":"subtract","params":[42,23],"id":5}
433 {"jsonrpc":"2.0","method":"subtract","params":[23,42],"id":6}
434 {"jsonrpc":"2.0","method":"subtract","params":[42,23],"id":7}
435 {"jsonrpc":"2.0","method":"subtract","params":[23,42],"id":8}
436 {"jsonrpc":"2.0","method":"subtract","params":[42,23],"id":9}
437 {"jsonrpc":"2.0","method":"subtract","params":[23,42],"id":10}
438
439 "#;
440
441 let mut cursor = BufReader::new(data.as_bytes());
442 let mut stream = from_async_read::<serde_json::Value, _>(&mut cursor);
443
444 for i in 1..=10 {
445 let item = stream.next().await.unwrap();
446 assert_eq!(
447 item,
448 serde_json::json!({
449 "jsonrpc": "2.0",
450 "method": "subtract",
451 "params": if i % 2 != 0 { [42, 23] } else { [23, 42] },
452 "id": i,
453 })
454 );
455 }
456 }
457
458 #[tokio::test]
459 async fn test_encode() {
460 let test_messages = vec![
461 serde_json::json!({
462 "jsonrpc": "2.0",
463 "method": "subtract",
464 "params": [42, 23],
465 "id": 1,
466 }),
467 serde_json::json!({
468 "jsonrpc": "2.0",
469 "method": "subtract",
470 "params": [23, 42],
471 "id": 2,
472 }),
473 ];
474
475 let mut buffer = Vec::new();
477 let mut writer = from_async_write(&mut buffer);
478
479 for message in test_messages.iter() {
481 writer.send(message.clone()).await.unwrap();
482 }
483 writer.close().await.unwrap();
484 drop(writer);
485 let output = String::from_utf8_lossy(&buffer);
487 let mut lines = output.lines();
488
489 for expected_message in test_messages {
490 let line = lines.next().unwrap();
491 let parsed_message: serde_json::Value = serde_json::from_str(line).unwrap();
492 assert_eq!(parsed_message, expected_message);
493 }
494
495 assert!(lines.next().is_none());
497 }
498
499 #[test]
500 fn test_standard_notification_check() {
501 assert!(is_standard_notification("notifications/cancelled"));
503 assert!(is_standard_notification("notifications/initialized"));
504 assert!(is_standard_notification("notifications/progress"));
505 assert!(is_standard_notification(
506 "notifications/resources/list_changed"
507 ));
508 assert!(is_standard_notification("notifications/resources/updated"));
509 assert!(is_standard_notification(
510 "notifications/prompts/list_changed"
511 ));
512 assert!(is_standard_notification("notifications/tools/list_changed"));
513 assert!(is_standard_notification("notifications/message"));
514 assert!(is_standard_notification("notifications/roots/list_changed"));
515
516 assert!(!is_standard_notification("notifications/stderr"));
518 assert!(!is_standard_notification("notifications/custom"));
519 assert!(!is_standard_notification("notifications/debug"));
520 assert!(!is_standard_notification("some/other/method"));
521 }
522
523 #[test]
524 fn test_compatibility_function() {
525 let stderr_message =
527 r#"{"method":"notifications/stderr","params":{"content":"stderr message"}}"#;
528 let custom_message = r#"{"method":"notifications/custom","params":{"data":"custom"}}"#;
529 let standard_message =
530 r#"{"method":"notifications/message","params":{"level":"info","data":"standard"}}"#;
531 let progress_message = r#"{"method":"notifications/progress","params":{"progressToken":"token","progress":50}}"#;
532
533 let result1 =
535 try_parse_with_compatibility::<serde_json::Value>(stderr_message.as_bytes(), "test");
536 let result2 =
537 try_parse_with_compatibility::<serde_json::Value>(custom_message.as_bytes(), "test");
538 let result3 =
539 try_parse_with_compatibility::<serde_json::Value>(standard_message.as_bytes(), "test");
540 let result4 =
541 try_parse_with_compatibility::<serde_json::Value>(progress_message.as_bytes(), "test");
542
543 assert!(result1.is_ok());
545 assert!(result2.is_ok());
546 assert!(result3.is_ok());
547 assert!(result4.is_ok());
548
549 assert!(result3.unwrap().is_some());
551 assert!(result4.unwrap().is_some());
552
553 println!("Standard notifications are preserved, non-standard are handled gracefully");
554 }
555}