use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use futures::future::BoxFuture;
use serde_json::json;
use tower::Service;
use turbomcp_core::{JsonRpcMessage, ProtocolVersion, meta};
use turbomcp_protocol::{methods, version};
use turbomcp_service::ProtocolError;
use uuid::Uuid;
pub struct LegacySessionAdapter<S> {
inner: S,
session: Arc<Mutex<Option<Session>>>,
}
#[derive(Clone)]
struct Session {
id: String,
version: ProtocolVersion,
}
impl<S> LegacySessionAdapter<S> {
pub fn new(inner: S) -> Self {
Self {
inner,
session: Arc::new(Mutex::new(None)),
}
}
}
impl<S: Clone> Clone for LegacySessionAdapter<S> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
session: Arc::clone(&self.session),
}
}
}
impl<S> Service<JsonRpcMessage> for LegacySessionAdapter<S>
where
S: Service<JsonRpcMessage, Response = Option<JsonRpcMessage>, Error = ProtocolError>,
S::Future: Send + 'static,
{
type Response = Option<JsonRpcMessage>;
type Error = ProtocolError;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, mut msg: JsonRpcMessage) -> Self::Future {
let is_initialize = matches!(
&msg,
JsonRpcMessage::Request(r) if r.method == methods::request::INITIALIZE
);
if is_initialize {
let candidate = Uuid::new_v4().to_string();
meta::set_request_meta(&mut msg, meta::internal::SESSION_ID, json!(candidate));
let session = Arc::clone(&self.session);
let fut = self.inner.call(msg);
return Box::pin(async move {
let out = fut.await?;
if let Some(JsonRpcMessage::Response(resp)) = &out
&& !resp.is_error()
{
let version = resp
.result
.as_ref()
.and_then(|r| r.get("protocolVersion"))
.and_then(serde_json::Value::as_str)
.map_or(ProtocolVersion::V2025_11_25, ProtocolVersion::from_wire);
*session.lock().expect("session state lock poisoned") = Some(Session {
id: candidate,
version,
});
}
Ok(out)
});
}
let session = self
.session
.lock()
.expect("session state lock poisoned")
.clone();
if let Some(session) = session {
let params = match &msg {
JsonRpcMessage::Request(r) => r.params.as_ref(),
JsonRpcMessage::Notification(n) => n.params.as_ref(),
JsonRpcMessage::Response(_) => None,
};
if version::request_protocol_version(params).is_none() {
meta::set_request_meta(
&mut msg,
meta::keys::PROTOCOL_VERSION,
json!(session.version.as_str()),
);
meta::set_request_meta(&mut msg, meta::internal::SESSION_ID, json!(session.id));
}
}
Box::pin(self.inner.call(msg))
}
}