#[cfg(feature = "mcp")]
use rmcp::transport::{TokioChildProcess, SseClientTransport};
use std::collections::HashMap;
use tokio::process::Command;
use crate::mcp::{
config::McpTransportConfig,
error::{McpError, McpResult},
};
pub enum McpTransport {
#[cfg(feature = "mcp")]
Stdio(TokioChildProcess),
#[cfg(feature = "mcp")]
Sse(SseClientTransport<reqwest::Client>),
#[cfg(not(feature = "mcp"))]
Disabled,
}
impl McpTransport {
pub async fn from_config(
config: &McpTransportConfig,
env_vars: HashMap<String, String>,
working_dir: Option<&std::path::Path>,
) -> McpResult<Self> {
match config {
McpTransportConfig::Stdio { command, args } => {
Self::create_stdio_transport(command, args, env_vars, working_dir).await
}
McpTransportConfig::Sse { url, headers } => {
Self::create_sse_transport(url, headers).await
}
}
}
#[cfg(feature = "mcp")]
async fn create_stdio_transport(
command: &str,
args: &[String],
env_vars: HashMap<String, String>,
working_dir: Option<&std::path::Path>,
) -> McpResult<Self> {
let mut cmd = Command::new(command);
for arg in args {
cmd.arg(arg);
}
if let Some(dir) = working_dir {
cmd.current_dir(dir);
}
for (key, value) in env_vars {
cmd.env(key, value);
}
let transport = TokioChildProcess::new(cmd)
.map_err(|e| McpError::transport(format!("Failed to create child process: {}", e)))?;
Ok(McpTransport::Stdio(transport))
}
#[cfg(not(feature = "mcp"))]
async fn create_stdio_transport(
_command: &str,
_args: &[String],
_env_vars: HashMap<String, String>,
_working_dir: Option<&std::path::Path>,
) -> McpResult<Self> {
Err(McpError::configuration("MCP feature not enabled"))
}
#[cfg(feature = "mcp")]
async fn create_sse_transport(
url: &str,
headers: &HashMap<String, String>,
) -> McpResult<Self> {
let transport = SseClientTransport::start(url).await
.map_err(|e| McpError::transport(format!("Failed to create SSE transport: {}", e)))?;
Ok(McpTransport::Sse(transport))
}
#[cfg(not(feature = "mcp"))]
async fn create_sse_transport(
_url: &str,
_headers: &HashMap<String, String>,
) -> McpResult<Self> {
Err(McpError::configuration("MCP feature not enabled"))
}
pub fn description(&self) -> String {
match self {
#[cfg(feature = "mcp")]
McpTransport::Stdio(_) => "stdio".to_string(),
#[cfg(feature = "mcp")]
McpTransport::Sse(_) => "sse".to_string(),
#[cfg(not(feature = "mcp"))]
McpTransport::Disabled => "disabled".to_string(),
}
}
pub fn is_stdio(&self) -> bool {
matches!(self, McpTransport::Stdio(_))
}
pub fn is_sse(&self) -> bool {
matches!(self, McpTransport::Sse(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mcp::config::McpTransportConfig;
#[test]
fn test_transport_description() {
#[cfg(not(feature = "mcp"))]
{
let transport = McpTransport::Disabled;
assert_eq!(transport.description(), "disabled");
}
}
#[test]
fn test_transport_type_checks() {
#[cfg(not(feature = "mcp"))]
{
let transport = McpTransport::Disabled;
assert!(!transport.is_stdio());
assert!(!transport.is_sse());
}
}
}