Skip to main content

mcp_kit/transport/
stdio.rs

1use crate::error::McpResult;
2use crate::server::{core::McpServer, session::Session};
3/// stdio transport — reads from stdin, writes to stdout (newline-delimited JSON).
4use futures_util::{SinkExt, StreamExt};
5use std::future::Future;
6use tokio::io::{stdin, stdout};
7use tokio_util::codec::{FramedRead, FramedWrite};
8use tracing::{debug, error};
9
10use super::codec::NdJsonCodec;
11
12/// Serves `server` over stdin/stdout until the stream closes.
13pub struct StdioTransport {
14    server: McpServer,
15}
16
17impl StdioTransport {
18    pub fn new(server: McpServer) -> Self {
19        Self { server }
20    }
21
22    pub async fn serve(self) -> McpResult<()> {
23        let mut reader = FramedRead::new(stdin(), NdJsonCodec);
24        let mut writer = FramedWrite::new(stdout(), NdJsonCodec);
25        let mut session = Session::new();
26
27        tracing::info!(server = %self.server.info().name, "stdio transport started");
28
29        while let Some(msg) = reader.next().await {
30            match msg {
31                Ok(msg) => {
32                    debug!(?msg, "Received message");
33                    if let Some(response) = self.server.handle_message(msg, &mut session).await {
34                        debug!(?response, "Sending response");
35                        if let Err(e) = writer.send(response).await {
36                            error!(error = %e, "Failed to write response");
37                            break;
38                        }
39                    }
40                }
41                Err(e) => {
42                    error!(error = %e, "Failed to decode message");
43                }
44            }
45        }
46
47        tracing::info!("stdio transport closed");
48        Ok(())
49    }
50}
51
52/// Extension trait that adds `.serve_stdio()` to `McpServer`.
53pub trait ServeStdioExt {
54    fn serve_stdio(self) -> impl Future<Output = McpResult<()>> + Send;
55}
56
57impl ServeStdioExt for McpServer {
58    fn serve_stdio(self) -> impl Future<Output = McpResult<()>> + Send {
59        StdioTransport::new(self).serve()
60    }
61}