use async_trait::async_trait;
use tokio::sync::mpsc;
#[cfg(feature = "stateless")]
use std::sync::{Arc, Mutex as StdMutex};
use crate::context::{NotificationReceiver, notification_channel};
use crate::error::Result;
use crate::jsonrpc::JsonRpcService;
use crate::protocol::{JsonRpcRequest, JsonRpcResponse, McpNotification};
use crate::router::{McpRouter, RouterRequest, RouterResponse};
use crate::transport::service::{CatchError, InjectAnnotations};
#[cfg(feature = "stateless")]
use crate::transport::stdio::{StdioSubscriptionInput, StdioSubscriptions};
use tower_service::Service;
use super::transport::ClientTransport;
pub struct ChannelTransport {
request_tx: mpsc::Sender<String>,
response_rx: mpsc::Receiver<String>,
connected: bool,
}
impl ChannelTransport {
pub fn new(router: McpRouter) -> Self {
let (notification_tx, notification_rx) = notification_channel(64);
let router = router.with_notification_sender(notification_tx);
Self::with_notifications(router, notification_rx)
}
pub fn with_notifications(router: McpRouter, notification_rx: NotificationReceiver) -> Self {
let service = JsonRpcService::new(router.clone());
Self::spawn_with_service(router, service, notification_rx)
}
pub fn layer<L>(router: McpRouter, layer: L) -> Self
where
L: tower::Layer<McpRouter>,
L::Service: Service<RouterRequest, Response = RouterResponse> + Clone + Send + 'static,
<L::Service as Service<RouterRequest>>::Error: std::fmt::Display + Send,
<L::Service as Service<RouterRequest>>::Future: Send,
{
let (notification_tx, notification_rx) = notification_channel(64);
let router = router.with_notification_sender(notification_tx);
Self::layer_with_notifications(router, layer, notification_rx)
}
pub fn layer_with_notifications<L>(
router: McpRouter,
layer: L,
notification_rx: NotificationReceiver,
) -> Self
where
L: tower::Layer<McpRouter>,
L::Service: Service<RouterRequest, Response = RouterResponse> + Clone + Send + 'static,
<L::Service as Service<RouterRequest>>::Error: std::fmt::Display + Send,
<L::Service as Service<RouterRequest>>::Future: Send,
{
let annotations = router.tool_annotations_map();
let wrapped = layer.layer(router.clone());
let service = InjectAnnotations::new(CatchError::new(wrapped), annotations);
Self::spawn_with_service(router, JsonRpcService::new(service), notification_rx)
}
fn spawn_with_service<S>(
router: McpRouter,
service: JsonRpcService<S>,
mut notification_rx: NotificationReceiver,
) -> Self
where
S: Service<RouterRequest, Response = RouterResponse, Error = std::convert::Infallible>
+ Clone
+ Send
+ 'static,
S::Future: Send,
{
let (request_tx, mut request_rx) = mpsc::channel::<String>(64);
let (response_tx, response_rx) = mpsc::channel::<String>(64);
#[cfg(feature = "stateless")]
let subscriptions = Arc::new(StdMutex::new(
StdioSubscriptions::new(Some(router.implementation()))
.with_observer(router.subscription_observer()),
));
let notification_out = response_tx.clone();
#[cfg(feature = "stateless")]
let notification_subscriptions = subscriptions.clone();
tokio::spawn(async move {
while let Some(notification) = notification_rx.recv().await {
#[cfg(feature = "stateless")]
{
let frames = notification_subscriptions
.lock()
.ok()
.and_then(|subscriptions| subscriptions.route_notification(¬ification));
if let Some(frames) = frames {
for frame in frames {
if notification_out.send(frame).await.is_err() {
return;
}
}
continue;
}
}
if let Some(json) = crate::transport::stdio::serialize_notification(¬ification)
&& notification_out.send(json).await.is_err()
{
break; }
}
});
tokio::spawn(async move {
while let Some(raw_request) = request_rx.recv().await {
let parsed: serde_json::Value = match serde_json::from_str(&raw_request) {
Ok(v) => v,
Err(e) => {
tracing::error!("ChannelTransport: failed to parse frame: {}", e);
continue;
}
};
#[cfg(feature = "stateless")]
{
let handled = subscriptions
.lock()
.ok()
.map(|mut subscriptions| subscriptions.handle_input(&service, &parsed));
match handled {
Some(Ok(StdioSubscriptionInput::Handled(frames))) => {
for frame in frames {
if response_tx.send(frame).await.is_err() {
return;
}
}
continue;
}
Some(Ok(StdioSubscriptionInput::Dispatch(request))) => {
let request_id = request.id.clone();
let response = crate::transport::stdio::dispatch_listen_request(
&mut service.clone(),
*request,
request_id.clone(),
)
.await;
let frames = subscriptions.lock().ok().map(|mut subscriptions| {
subscriptions.complete_listen(request_id, &response)
});
if let Some(Ok(frames)) = frames {
for frame in frames {
if response_tx.send(frame).await.is_err() {
return;
}
}
}
continue;
}
_ => {}
}
}
if parsed.get("id").is_none() {
if parsed.get("method").and_then(|m| m.as_str())
== Some("notifications/initialized")
{
router.handle_notification(McpNotification::Initialized);
}
continue;
}
let req: JsonRpcRequest = match serde_json::from_value(parsed) {
Ok(r) => r,
Err(e) => {
tracing::error!("ChannelTransport: failed to parse request: {}", e);
continue;
}
};
let mut service = service.clone();
let response_out = response_tx.clone();
tokio::spawn(async move {
let response = service.call_single(req).await;
let json = match response {
Ok(resp) => match serde_json::to_string(&resp) {
Ok(j) => j,
Err(e) => {
tracing::error!(
"ChannelTransport: failed to serialize response: {}",
e
);
return;
}
},
Err(e) => {
let err_resp = JsonRpcResponse::error(
None,
tower_mcp_types::JsonRpcError::internal_error(e.to_string()),
);
match serde_json::to_string(&err_resp) {
Ok(j) => j,
Err(_) => return,
}
}
};
let _ = response_out.send(json).await;
});
}
#[cfg(feature = "stateless")]
if let Ok(mut subscriptions) = subscriptions.lock() {
subscriptions.drain_disconnected();
}
});
Self {
request_tx,
response_rx,
connected: true,
}
}
}
#[async_trait]
impl ClientTransport for ChannelTransport {
async fn send(&mut self, message: &str) -> Result<()> {
self.request_tx
.send(message.to_string())
.await
.map_err(|_| crate::error::Error::internal("ChannelTransport: server task dropped"))?;
Ok(())
}
async fn recv(&mut self) -> Result<Option<String>> {
match self.response_rx.recv().await {
Some(msg) => Ok(Some(msg)),
None => {
self.connected = false;
Ok(None)
}
}
}
fn is_connected(&self) -> bool {
self.connected
}
async fn close(&mut self) -> Result<()> {
self.connected = false;
Ok(())
}
}