rmcp_in_process_transport/
in_process.rs1use std::{error::Error, fmt, marker::PhantomData};
2
3use futures::{Sink, Stream};
4use tokio::sync::mpsc::{self, Receiver, Sender};
5
6use rmcp::{
7 Service, serve_client, serve_server,
8 service::{
9 ClientInitializeError, RoleClient, RoleServer, RunningService, RxJsonRpcMessage,
10 ServerInitializeError, ServiceRole, TxJsonRpcMessage,
11 },
12 transport::{IntoTransport, sink_stream::SinkStreamTransport},
13};
14
15#[derive(Debug)]
16pub struct InProcessTransportError(String);
17
18impl fmt::Display for InProcessTransportError {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 write!(f, "InProcess transport error: {}", self.0)
21 }
22}
23
24impl Error for InProcessTransportError {}
25
26pub struct ClientSendError(mpsc::error::SendError<TxJsonRpcMessage<RoleClient>>);
27pub struct ServerSendError(mpsc::error::SendError<TxJsonRpcMessage<RoleServer>>);
28
29impl From<ClientSendError> for InProcessTransportError {
30 fn from(err: ClientSendError) -> Self {
31 InProcessTransportError(format!("Failed to send client message: {}", err.0))
32 }
33}
34
35impl From<ServerSendError> for InProcessTransportError {
36 fn from(err: ServerSendError) -> Self {
37 InProcessTransportError(format!("Failed to send server message: {}", err.0))
38 }
39}
40
41impl From<InProcessTransportError> for std::io::Error {
42 fn from(value: InProcessTransportError) -> Self {
43 std::io::Error::other(value)
44 }
45}
46
47impl From<std::io::Error> for InProcessTransportError {
48 fn from(err: std::io::Error) -> Self {
49 InProcessTransportError(format!("IO error: {}", err))
50 }
51}
52
53impl From<ClientInitializeError> for InProcessTransportError {
54 fn from(err: ClientInitializeError) -> Self {
55 InProcessTransportError(format!("Client initialization error: {}", err))
56 }
57}
58
59impl From<ServerInitializeError> for InProcessTransportError {
60 fn from(err: ServerInitializeError) -> Self {
61 InProcessTransportError(format!("Server initialization error: {}", err))
62 }
63}
64
65pub fn create_in_process_transport_pair(
71 buffer_size: usize,
72) -> (
73 InProcessTransport<RoleClient>,
74 InProcessTransport<RoleServer>,
75) {
76 let (client_tx, server_rx) = mpsc::channel::<TxJsonRpcMessage<RoleClient>>(buffer_size);
78 let (server_tx, client_rx) = mpsc::channel::<TxJsonRpcMessage<RoleServer>>(buffer_size);
80
81 let client_transport = InProcessTransport::<RoleClient> {
82 tx: client_tx,
83 rx: client_rx,
84 _phantom: PhantomData,
85 };
86
87 let server_transport = InProcessTransport::<RoleServer> {
88 tx: server_tx,
89 rx: server_rx,
90 _phantom: PhantomData,
91 };
92
93 (client_transport, server_transport)
94}
95
96pub fn create_transport_pair(
98 buffer_size: usize,
99) -> (
100 InProcessTransport<RoleClient>,
101 InProcessTransport<RoleServer>,
102) {
103 create_in_process_transport_pair(buffer_size)
104}
105
106pub struct InProcessTransport<R: ServiceRole> {
109 tx: Sender<TxJsonRpcMessage<R>>,
110 rx: Receiver<RxJsonRpcMessage<R>>,
111 _phantom: PhantomData<R>,
112}
113
114impl<R: ServiceRole> InProcessTransport<R> {
115 pub fn split(self) -> (InProcessSink<R>, InProcessStream<R>) {
117 let sink = InProcessSink {
118 tx: self.tx,
119 _phantom: PhantomData,
120 pending: None,
121 };
122 let stream = InProcessStream {
123 rx: self.rx,
124 _phantom: PhantomData,
125 };
126 (sink, stream)
127 }
128
129 pub fn new(
131 buffer_size: usize,
132 ) -> (
133 InProcessTransport<RoleClient>,
134 InProcessTransport<RoleServer>,
135 ) {
136 create_in_process_transport_pair(buffer_size)
137 }
138}
139
140pub trait InProcessTransportExt<R: ServiceRole> {
142 type Error;
144
145 fn serve<S>(
147 self,
148 service: S,
149 ) -> impl std::future::Future<Output = Result<RunningService<R, S>, Self::Error>> + Send
150 where
151 S: Service<R> + Send + 'static;
152}
153
154impl InProcessTransportExt<RoleClient> for InProcessTransport<RoleClient> {
155 type Error = ClientInitializeError;
156
157 async fn serve<S>(
158 self,
159 service: S,
160 ) -> Result<RunningService<RoleClient, S>, ClientInitializeError>
161 where
162 S: Service<RoleClient> + Send + 'static,
163 {
164 serve_client(service, self).await
165 }
166}
167
168impl InProcessTransportExt<RoleServer> for InProcessTransport<RoleServer> {
169 type Error = ServerInitializeError;
170
171 async fn serve<S>(
172 self,
173 service: S,
174 ) -> Result<RunningService<RoleServer, S>, ServerInitializeError>
175 where
176 S: Service<RoleServer> + Send + 'static,
177 {
178 serve_server(service, self).await
179 }
180}
181
182pin_project_lite::pin_project! {
183 pub struct InProcessSink<R: ServiceRole> {
185 tx: Sender<TxJsonRpcMessage<R>>,
186 _phantom: PhantomData<R>,
187 pending: Option<TxJsonRpcMessage<R>>,
189 }
190}
191
192pin_project_lite::pin_project! {
193 pub struct InProcessStream<R: ServiceRole> {
195 #[pin]
196 rx: Receiver<RxJsonRpcMessage<R>>,
197 _phantom: PhantomData<R>,
198 }
199}
200
201impl<R: ServiceRole> Sink<TxJsonRpcMessage<R>> for InProcessSink<R> {
202 type Error = InProcessTransportError;
203
204 fn poll_ready(
205 self: std::pin::Pin<&mut Self>,
206 cx: &mut std::task::Context<'_>,
207 ) -> std::task::Poll<Result<(), Self::Error>> {
208 let this = self.project();
209
210 if let Some(pending) = this.pending.take() {
212 match this.tx.try_send(pending) {
213 Ok(()) => {
214 std::task::Poll::Ready(Ok(()))
216 }
217 Err(tokio::sync::mpsc::error::TrySendError::Full(msg)) => {
218 *this.pending = Some(msg);
220 cx.waker().wake_by_ref();
222 std::task::Poll::Pending
223 }
224 Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => std::task::Poll::Ready(
225 Err(InProcessTransportError("Channel closed".to_string())),
226 ),
227 }
228 } else {
229 if this.tx.is_closed() {
231 std::task::Poll::Ready(Err(InProcessTransportError("Channel closed".to_string())))
232 } else {
233 std::task::Poll::Ready(Ok(()))
235 }
236 }
237 }
238
239 fn start_send(
240 self: std::pin::Pin<&mut Self>,
241 item: TxJsonRpcMessage<R>,
242 ) -> Result<(), Self::Error> {
243 let this = self.project();
244
245 match this.tx.try_send(item) {
247 Ok(()) => Ok(()),
248 Err(tokio::sync::mpsc::error::TrySendError::Full(msg)) => {
249 *this.pending = Some(msg);
251 Ok(())
252 }
253 Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
254 Err(InProcessTransportError("Channel closed".to_string()))
255 }
256 }
257 }
258
259 fn poll_flush(
260 self: std::pin::Pin<&mut Self>,
261 _cx: &mut std::task::Context<'_>,
262 ) -> std::task::Poll<Result<(), Self::Error>> {
263 std::task::Poll::Ready(Ok(()))
265 }
266
267 fn poll_close(
268 self: std::pin::Pin<&mut Self>,
269 _cx: &mut std::task::Context<'_>,
270 ) -> std::task::Poll<Result<(), Self::Error>> {
271 std::task::Poll::Ready(Ok(()))
273 }
274}
275
276impl<R: ServiceRole> Stream for InProcessStream<R> {
277 type Item = RxJsonRpcMessage<R>;
278
279 fn poll_next(
280 self: std::pin::Pin<&mut Self>,
281 cx: &mut std::task::Context<'_>,
282 ) -> std::task::Poll<Option<Self::Item>> {
283 self.project().rx.poll_recv(cx)
284 }
285}
286
287pub enum TransportAdapterInProcess {}
289
290impl<R> IntoTransport<R, InProcessTransportError, TransportAdapterInProcess>
292 for InProcessTransport<R>
293where
294 R: ServiceRole + 'static + Unpin,
295 R::Req: Unpin,
296 R::Resp: Unpin,
297 R::Not: Unpin,
298{
299 fn into_transport(
300 self,
301 ) -> impl rmcp::transport::Transport<R, Error = InProcessTransportError> + 'static {
302 let (sink, stream) = self.split();
303 SinkStreamTransport::new(sink, stream)
304 }
305}
306
307pub struct TokioInProcess {
310 client_transport: InProcessTransport<RoleClient>,
311 _server_task: tokio::task::JoinHandle<()>,
312}
313
314impl TokioInProcess {
315 pub async fn new<S>(service: S) -> Result<Self, InProcessTransportError>
317 where
318 S: Service<RoleServer> + Send + 'static,
319 {
320 Self::with_buffer_size(service, 32).await
321 }
322
323 pub async fn with_buffer_size<S>(
325 service: S,
326 buffer_size: usize,
327 ) -> Result<Self, InProcessTransportError>
328 where
329 S: Service<RoleServer> + Send + 'static,
330 {
331 let (client_transport, server_transport) = create_in_process_transport_pair(buffer_size);
333
334 let server_handle = tokio::spawn(async move {
336 tracing::debug!("Server task starting");
337 match server_transport.serve(service).await {
338 Ok(running_service) => {
339 tracing::debug!("Server initialized successfully, keeping it alive");
340 if let Err(e) = running_service.waiting().await {
342 tracing::error!("Server service error: {:?}", e);
343 } else {
344 tracing::debug!("Server service completed normally");
345 }
346 }
347 Err(e) => {
348 tracing::error!("Server initialization error: {:?}", e);
349 }
350 }
351 });
352
353 Ok(Self {
354 client_transport,
355 _server_task: server_handle,
356 })
357 }
358}
359
360impl IntoTransport<RoleClient, InProcessTransportError, TransportAdapterInProcess>
361 for TokioInProcess
362{
363 fn into_transport(
364 self,
365 ) -> impl rmcp::transport::Transport<RoleClient, Error = InProcessTransportError> + 'static
366 {
367 tracing::debug!("Moving server task to background keeper");
369 tokio::spawn(async move {
370 tracing::debug!("Background keeper task started");
371 if let Err(e) = self._server_task.await {
372 tracing::error!("Server task failed: {:?}", e);
373 } else {
374 tracing::debug!("Background server task completed normally");
375 }
376 });
377
378 self.client_transport.into_transport()
379 }
380}