mod http;
mod stdio;
pub use http::McpHttpClient;
pub use stdio::McpStdioClient;
use crate::error::McpError;
use crate::proto::{McpToolDef, ToolOutput};
use serde_json::Value;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct McpClientOptions {
pub timeout: Duration,
pub client_name: String,
pub client_version: String,
}
impl Default for McpClientOptions {
fn default() -> Self {
Self {
timeout: Duration::from_secs(30),
client_name: "greentic-mcp-client".to_string(),
client_version: env!("CARGO_PKG_VERSION").to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServerInfo {
pub name: String,
pub version: String,
pub protocol_version: String,
}
impl ServerInfo {
pub(crate) fn from_initialize_result(result: &Value) -> Result<Self, McpError> {
let server_info = result.get("serverInfo").ok_or_else(|| {
McpError::BadInitialize("missing serverInfo in initialize result".to_string())
})?;
Ok(Self {
name: server_info
.get("name")
.and_then(Value::as_str)
.unwrap_or("")
.to_string(),
version: server_info
.get("version")
.and_then(Value::as_str)
.unwrap_or("")
.to_string(),
protocol_version: result
.get("protocolVersion")
.and_then(Value::as_str)
.unwrap_or("")
.to_string(),
})
}
}
#[allow(async_fn_in_trait)]
pub trait McpClient {
async fn initialize(&mut self) -> Result<ServerInfo, McpError>;
async fn list_tools(&mut self) -> Result<Vec<McpToolDef>, McpError>;
async fn call_tool(&mut self, name: &str, args: &Value) -> Result<ToolOutput, McpError>;
}