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;
#[cfg(feature = "stateless")]
use crate::transport::stdio::{StdioSubscriptionInput, StdioSubscriptions};
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,
mut notification_rx: NotificationReceiver,
) -> Self {
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()),
router.final_tasks_enabled(),
)));
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; }
}
});
let service = JsonRpcService::new(router.clone());
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));
if let Some(Ok(StdioSubscriptionInput::Handled(frames))) = handled {
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;
});
}
});
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(())
}
}