use mcpkit_core::error::McpError;
use mcpkit_core::types::{
CreateMessageRequest, CreateMessageResult, ElicitRequest, ElicitResult, TaskId, TaskProgress,
};
use std::future::Future;
pub trait ClientHandler: Send + Sync {
fn create_message(
&self,
_request: CreateMessageRequest,
) -> impl Future<Output = Result<CreateMessageResult, McpError>> + Send {
async {
Err(McpError::CapabilityNotSupported {
capability: "sampling".to_string(),
available: Box::new([]),
})
}
}
fn elicit(
&self,
_request: ElicitRequest,
) -> impl Future<Output = Result<ElicitResult, McpError>> + Send {
async {
Err(McpError::CapabilityNotSupported {
capability: "elicitation".to_string(),
available: Box::new([]),
})
}
}
fn list_roots(&self) -> impl Future<Output = Result<Vec<Root>, McpError>> + Send {
async {
Err(McpError::CapabilityNotSupported {
capability: "roots".to_string(),
available: Box::new([]),
})
}
}
fn on_connected(&self) -> impl Future<Output = ()> + Send {
async {}
}
fn on_disconnected(&self) -> impl Future<Output = ()> + Send {
async {}
}
fn on_task_progress(
&self,
_task_id: TaskId,
_progress: TaskProgress,
) -> impl Future<Output = ()> + Send {
async {}
}
fn on_resource_updated(&self, _uri: String) -> impl Future<Output = ()> + Send {
async {}
}
fn on_resources_list_changed(&self) -> impl Future<Output = ()> + Send {
async {}
}
fn on_tools_list_changed(&self) -> impl Future<Output = ()> + Send {
async {}
}
fn on_prompts_list_changed(&self) -> impl Future<Output = ()> + Send {
async {}
}
}
#[derive(Debug, Clone)]
pub struct Root {
pub uri: String,
pub name: Option<String>,
}
impl Root {
pub fn new(uri: impl Into<String>) -> Self {
Self {
uri: uri.into(),
name: None,
}
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
pub struct NoOpHandler;
impl ClientHandler for NoOpHandler {}
pub struct SamplingHandler<F> {
handler: F,
}
impl<F, Fut> SamplingHandler<F>
where
F: Fn(CreateMessageRequest) -> Fut + Send + Sync,
Fut: Future<Output = Result<CreateMessageResult, McpError>> + Send,
{
pub const fn new(handler: F) -> Self {
Self { handler }
}
}
impl<F, Fut> ClientHandler for SamplingHandler<F>
where
F: Fn(CreateMessageRequest) -> Fut + Send + Sync,
Fut: Future<Output = Result<CreateMessageResult, McpError>> + Send,
{
fn create_message(
&self,
request: CreateMessageRequest,
) -> impl Future<Output = Result<CreateMessageResult, McpError>> + Send {
(self.handler)(request)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_root_builder() {
let root = Root::new("file:///home/user/project").name("My Project");
assert!(root.uri.contains("project"));
assert_eq!(root.name, Some("My Project".to_string()));
}
#[tokio::test]
async fn test_noop_handler() {
let handler = NoOpHandler;
let result = handler
.create_message(CreateMessageRequest {
messages: vec![],
model_preferences: None,
system_prompt: None,
include_context: None,
temperature: None,
max_tokens: 100,
stop_sequences: None,
metadata: None,
})
.await;
assert!(result.is_err());
}
}