use rmcp::ServiceExt;
use rmcp::service::ServerInitializeError;
use tokio_util::sync::CancellationToken;
use super::Adapter;
#[derive(Debug, thiserror::Error)]
pub enum ServeError {
#[error("the stdio transport could not start: {0}")]
Start(String),
#[error("the service task failed: {0}")]
Join(String),
}
pub async fn serve(adapter: Adapter, shutdown: CancellationToken) -> Result<(), ServeError> {
let observer = adapter.clone();
let started = tokio::select! {
biased;
() = shutdown.cancelled() => return Ok(()),
started = adapter.serve(rmcp::transport::stdio()) => started,
};
let running = match started {
Ok(running) => running,
Err(ServerInitializeError::ConnectionClosed(_)) if observer.has_been_discovered() => {
tracing::info!(
event = "shutdown",
reason = "input_closed",
"transport closed"
);
return Ok(());
}
Err(error) => return Err(ServeError::Start(error.to_string())),
};
let stop = running.cancellation_token();
tokio::spawn(async move {
shutdown.cancelled().await;
stop.cancel();
});
let reason = running
.waiting()
.await
.map_err(|e| ServeError::Join(e.to_string()))?;
tracing::info!(event = "shutdown", reason = ?reason, "transport closed");
Ok(())
}
#[cfg(test)]
mod tests {
#[test]
fn req_mcp_002_lim_o3_is_the_default_line_bound_of_the_protocol_library() {
assert_eq!(
rypsok_core::limits::defaults::STDIO_MAX_LINE_BYTES as usize,
rmcp::transport::async_rw::DEFAULT_MAX_LINE_LENGTH
);
}
}