use mocopr_core::prelude::*;
use mocopr_core::transport::{TransportConfig, TransportFactory};
use mocopr_core::utils::Utils;
use std::sync::Arc;
pub struct McpClient {
session: Arc<Session>,
info: Implementation,
capabilities: ClientCapabilities,
}
impl McpClient {
pub async fn new(
transport_config: TransportConfig,
client_info: Implementation,
client_capabilities: ClientCapabilities,
) -> Result<Self> {
let transport = TransportFactory::create(transport_config).await?;
let handler = Arc::new(DefaultMessageHandler::new(
Implementation {
name: "MoCoPr Client".to_string(),
version: "1.0.0".to_string(),
},
ServerCapabilities::default(),
));
let (session, _events) = Session::new(transport, handler);
let session = Arc::new(session);
session
.initialize(client_info.clone(), client_capabilities.clone())
.await?;
Ok(Self {
session,
info: client_info,
capabilities: client_capabilities,
})
}
pub async fn connect_stdio(
command: &str,
args: &[&str],
client_info: Implementation,
capabilities: ClientCapabilities,
) -> Result<Self> {
let transport = mocopr_core::transport::stdio::StdioTransport::spawn(command, args).await?;
let handler = Arc::new(DefaultMessageHandler::new(
Implementation {
name: "MoCoPr Client".to_string(),
version: "1.0.0".to_string(),
},
ServerCapabilities::default(),
));
let (session, _events) = Session::new(Box::new(transport), handler);
let session = Arc::new(session);
session
.initialize(client_info.clone(), capabilities.clone())
.await?;
Ok(Self {
session,
info: client_info,
capabilities,
})
}
pub async fn connect_websocket(
url: &str,
client_info: Implementation,
capabilities: ClientCapabilities,
) -> Result<Self> {
let transport = mocopr_core::transport::websocket::WebSocketTransport::new(url).await?;
let handler = Arc::new(DefaultMessageHandler::new(
Implementation {
name: "MoCoPr Client".to_string(),
version: "1.0.0".to_string(),
},
ServerCapabilities::default(),
));
let (session, _events) = Session::new(Box::new(transport), handler);
let session = Arc::new(session);
session
.initialize(client_info.clone(), capabilities.clone())
.await?;
Ok(Self {
session,
info: client_info,
capabilities,
})
}
pub async fn list_resources(&self) -> Result<ResourcesListResponse> {
let request = JsonRpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(Protocol::generate_request_id()),
method: "resources/list".to_string(),
params: Some(Utils::to_json_value(&ResourcesListRequest::new())?),
};
let response = self.session.send_request(request).await?;
if let Some(error) = response.error {
return Err(Error::Server(error.message));
}
let result = response
.result
.ok_or_else(|| Error::Server("Missing result in response".to_string()))?;
Utils::from_json_value(result)
}
pub async fn read_resource(&self, uri: url::Url) -> Result<ResourcesReadResponse> {
let request = JsonRpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(Protocol::generate_request_id()),
method: "resources/read".to_string(),
params: Some(Utils::to_json_value(&ResourcesReadRequest { uri })?),
};
let response = self.session.send_request(request).await?;
if let Some(error) = response.error {
return Err(Error::Server(error.message));
}
let result = response
.result
.ok_or_else(|| Error::Server("Missing result in response".to_string()))?;
Utils::from_json_value(result)
}
pub async fn list_tools(&self) -> Result<ToolsListResponse> {
let request = JsonRpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(Protocol::generate_request_id()),
method: "tools/list".to_string(),
params: Some(Utils::to_json_value(&ToolsListRequest::new())?),
};
let response = self.session.send_request(request).await?;
if let Some(error) = response.error {
return Err(Error::Server(error.message));
}
let result = response
.result
.ok_or_else(|| Error::Server("Missing result in response".to_string()))?;
Utils::from_json_value(result)
}
pub async fn call_tool(
&self,
name: String,
arguments: Option<serde_json::Value>,
) -> Result<ToolsCallResponse> {
let request = JsonRpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(Protocol::generate_request_id()),
method: "tools/call".to_string(),
params: Some(Utils::to_json_value(&ToolsCallRequest { name, arguments })?),
};
let response = self.session.send_request(request).await?;
if let Some(error) = response.error {
return Err(Error::Server(error.message));
}
let result = response
.result
.ok_or_else(|| Error::Server("Missing result in response".to_string()))?;
Utils::from_json_value(result)
}
pub async fn list_prompts(&self) -> Result<PromptsListResponse> {
let request = JsonRpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(Protocol::generate_request_id()),
method: "prompts/list".to_string(),
params: Some(Utils::to_json_value(&PromptsListRequest::new())?),
};
let response = self.session.send_request(request).await?;
if let Some(error) = response.error {
return Err(Error::Server(error.message));
}
let result = response
.result
.ok_or_else(|| Error::Server("Missing result in response".to_string()))?;
Utils::from_json_value(result)
}
pub async fn get_prompt(
&self,
name: String,
arguments: Option<std::collections::HashMap<String, String>>,
) -> Result<PromptsGetResponse> {
let request = JsonRpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(Protocol::generate_request_id()),
method: "prompts/get".to_string(),
params: Some(Utils::to_json_value(&PromptsGetRequest {
name,
arguments,
})?),
};
let response = self.session.send_request(request).await?;
if let Some(error) = response.error {
return Err(Error::Server(error.message));
}
let result = response
.result
.ok_or_else(|| Error::Server("Missing result in response".to_string()))?;
Utils::from_json_value(result)
}
pub async fn ping(&self, message: Option<String>) -> Result<PingResponse> {
let request = JsonRpcRequest {
jsonrpc: "2.0".to_string(),
id: Some(Protocol::generate_request_id()),
method: "ping".to_string(),
params: Some(Utils::to_json_value(&PingRequest { message })?),
};
let response = self.session.send_request(request).await?;
if let Some(error) = response.error {
return Err(Error::Server(error.message));
}
let result = response
.result
.ok_or_else(|| Error::Server("Missing result in response".to_string()))?;
Utils::from_json_value(result)
}
pub async fn close(&self) -> Result<()> {
self.session.close().await
}
pub async fn session_state(&self) -> SessionState {
self.session.state().await
}
pub async fn is_connected(&self) -> bool {
self.session.is_connected().await
}
pub fn client_info(&self) -> &Implementation {
&self.info
}
pub fn client_capabilities(&self) -> &ClientCapabilities {
&self.capabilities
}
}
pub struct McpClientBuilder {
client_info: Option<Implementation>,
capabilities: ClientCapabilities,
}
impl McpClientBuilder {
pub fn new() -> Self {
Self {
client_info: None,
capabilities: ClientCapabilities::default(),
}
}
pub fn with_info(mut self, name: String, version: String) -> Self {
self.client_info = Some(Implementation { name, version });
self
}
pub fn with_sampling(mut self) -> Self {
self.capabilities = self.capabilities.with_sampling();
self
}
pub fn with_roots(mut self, list_changed: bool) -> Self {
self.capabilities = self.capabilities.with_roots(list_changed);
self
}
pub fn with_experimental(mut self, key: String, value: serde_json::Value) -> Self {
self.capabilities = self.capabilities.with_experimental(key, value);
self
}
pub async fn connect_stdio(self, command: &str, args: &[&str]) -> Result<McpClient> {
let client_info = self
.client_info
.ok_or_else(|| Error::InvalidRequest("Client info is required".to_string()))?;
McpClient::connect_stdio(command, args, client_info, self.capabilities).await
}
pub async fn connect_websocket(self, url: &str) -> Result<McpClient> {
let client_info = self
.client_info
.ok_or_else(|| Error::InvalidRequest("Client info is required".to_string()))?;
McpClient::connect_websocket(url, client_info, self.capabilities).await
}
}
impl Default for McpClientBuilder {
fn default() -> Self {
Self::new()
}
}
pub mod prelude {
pub use crate::*;
pub use mocopr_core::prelude::*;
}