pub mod json_rpc;
mod stdio;
use crate::error::Result;
use async_trait::async_trait;
pub use json_rpc::{JsonRpcMessage, JsonRpcRequest, JsonRpcResponse};
use serde_json::Value;
pub use stdio::StdioTransport;
#[async_trait]
pub trait Transport: Send + Sync {
async fn initialize(&self) -> Result<()>;
async fn list_tools(&self) -> Result<Vec<Value>>;
async fn call_tool(&self, name: &str, args: Value) -> Result<Value>;
async fn list_resources(&self) -> Result<Vec<Value>>;
async fn get_resource(&self, uri: &str) -> Result<Value>;
}
pub fn create_transport_for_config(
server_name: &str,
server_config: &crate::config::ServerConfig,
) -> Result<StdioTransport> {
use crate::error::Error;
use async_process::{Command, Stdio as AsyncStdio};
use tracing;
tracing::debug!(server = %server_name, "Creating transport for server");
let mut cmd = Command::new(&server_config.command);
cmd.args(&server_config.args);
for (key, value) in &server_config.env {
cmd.env(key, value);
}
cmd.stdin(AsyncStdio::piped())
.stdout(AsyncStdio::piped())
.stderr(AsyncStdio::inherit());
let child = cmd.spawn().map_err(|e| {
tracing::error!(error = %e, "Failed to spawn server process");
Error::Process(format!("Failed to spawn server process: {}", e))
})?;
let stdin = child.stdin.ok_or_else(|| {
tracing::error!("Failed to open stdin for server process");
Error::Process("Failed to open stdin for server process".into())
})?;
let stdout = child.stdout.ok_or_else(|| {
tracing::error!("Failed to open stdout from server process");
Error::Process("Failed to open stdout from server process".into())
})?;
let transport = StdioTransport::new(server_name.to_string(), stdin, stdout);
Ok(transport)
}