use std::sync::Arc;
use synwire_core::agents::error::AgentError;
use synwire_core::mcp::traits::{
McpConnectionState, McpServerStatus, McpToolDescriptor, McpTransport,
};
pub struct McpClientSession {
name: String,
transport: Arc<dyn McpTransport>,
tool_cache: Vec<McpToolDescriptor>,
}
impl std::fmt::Debug for McpClientSession {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("McpClientSession")
.field("name", &self.name)
.field("tool_cache_len", &self.tool_cache.len())
.finish_non_exhaustive()
}
}
impl McpClientSession {
pub async fn connect(
name: impl Into<String>,
transport: Arc<dyn McpTransport>,
) -> Result<Self, AgentError> {
transport.connect().await?;
let name = name.into();
tracing::debug!(server = %name, "McpClientSession connected");
Ok(Self {
name,
transport,
tool_cache: Vec::new(),
})
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
pub async fn status(&self) -> McpServerStatus {
self.transport.status().await
}
pub async fn is_connected(&self) -> bool {
self.transport.status().await.state == McpConnectionState::Connected
}
#[must_use]
pub fn cached_tools(&self) -> &[McpToolDescriptor] {
&self.tool_cache
}
pub async fn populate_tool_cache(&mut self) -> Result<(), AgentError> {
self.tool_cache = self.transport.list_tools().await?;
tracing::debug!(
server = %self.name,
tools = self.tool_cache.len(),
"Tool cache populated"
);
Ok(())
}
#[must_use]
pub fn transport(&self) -> &Arc<dyn McpTransport> {
&self.transport
}
}
impl Drop for McpClientSession {
fn drop(&mut self) {
let transport = Arc::clone(&self.transport);
let name = self.name.clone();
let _handle = tokio::task::spawn(async move {
if let Err(e) = transport.disconnect().await {
tracing::warn!(server = %name, error = %e, "Error during McpClientSession drop disconnect");
}
});
}
}