pub mod elicitation;
pub mod roots;
pub mod sampling;
use crate::types::protocol::{ClientRequest, Request, ServerRequest};
use std::sync::Arc;
pub use elicitation::HostElicitationHandler;
pub use roots::RootsProvider;
pub use sampling::{
lift_content_to_sampling, lift_result_to_with_tools, ApprovalDecision, HostSamplingHandler,
HostSamplingHandlerWithTools, LegacyHostSamplingAdapter, PreflightApproval,
SamplingResultReview,
};
#[derive(Clone, Default)]
pub struct ClientHostRegistry {
pub(crate) sampling: Option<Arc<dyn HostSamplingHandler>>,
pub(crate) sampling_with_tools: Option<Arc<dyn HostSamplingHandlerWithTools>>,
pub(crate) elicitation: Option<Arc<dyn HostElicitationHandler>>,
pub(crate) roots: Option<RootsProvider>,
pub(crate) approval: Option<PreflightApproval>,
pub(crate) result_review: Option<SamplingResultReview>,
}
impl std::fmt::Debug for ClientHostRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ClientHostRegistry")
.field("has_sampling", &self.sampling.is_some())
.field(
"has_sampling_with_tools",
&self.sampling_with_tools.is_some(),
)
.field("has_elicitation", &self.elicitation.is_some())
.field("has_roots", &self.roots.is_some())
.field("has_approval", &self.approval.is_some())
.field("has_result_review", &self.result_review.is_some())
.finish()
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HostRequestKind {
Sampling,
Elicitation,
Roots,
Ping,
Unhandled,
}
#[doc(hidden)]
pub fn classify_host_request(request: &Request) -> HostRequestKind {
match request {
Request::Client(client) => match client.as_ref() {
ClientRequest::CreateMessage(_) => HostRequestKind::Sampling,
ClientRequest::Ping => HostRequestKind::Ping,
_ => HostRequestKind::Unhandled,
},
Request::Server(server) => match server.as_ref() {
ServerRequest::CreateMessage(_) => HostRequestKind::Sampling,
ServerRequest::ElicitationCreate(_) => HostRequestKind::Elicitation,
ServerRequest::ListRoots => HostRequestKind::Roots,
},
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::sampling::CreateMessageParams;
#[test]
fn registry_default_is_all_none() {
let reg = ClientHostRegistry::default();
assert!(reg.sampling.is_none());
assert!(reg.elicitation.is_none());
assert!(reg.roots.is_none());
assert!(reg.approval.is_none());
assert!(reg.result_review.is_none());
}
#[test]
fn registry_debug_reports_has_flags() {
let reg = ClientHostRegistry::default();
let dbg = format!("{reg:?}");
assert!(dbg.contains("has_sampling: false"));
assert!(dbg.contains("has_elicitation: false"));
assert!(dbg.contains("has_roots: false"));
}
#[test]
fn classify_sampling_server_variant() {
let req = Request::Server(Box::new(ServerRequest::CreateMessage(Box::new(
CreateMessageParams::new(Vec::new()),
))));
assert_eq!(classify_host_request(&req), HostRequestKind::Sampling);
}
#[test]
fn classify_sampling_client_alias_variant() {
let req = Request::Client(Box::new(ClientRequest::CreateMessage(Box::new(
CreateMessageParams::new(Vec::new()),
))));
assert_eq!(classify_host_request(&req), HostRequestKind::Sampling);
}
#[test]
fn classify_roots_and_elicitation() {
let roots = Request::Server(Box::new(ServerRequest::ListRoots));
assert_eq!(classify_host_request(&roots), HostRequestKind::Roots);
let elicit = Request::Server(Box::new(ServerRequest::ElicitationCreate(Box::new(
crate::types::elicitation::ElicitRequestParams::Form {
message: "please".to_string(),
requested_schema: serde_json::json!({}),
},
))));
assert_eq!(classify_host_request(&elicit), HostRequestKind::Elicitation);
}
#[test]
fn classify_unhandled_client_request() {
let req = Request::Client(Box::new(ClientRequest::ListTools(Default::default())));
assert_eq!(classify_host_request(&req), HostRequestKind::Unhandled);
}
#[test]
fn classify_inbound_ping_is_ping_not_unhandled() {
let req = Request::Client(Box::new(ClientRequest::Ping));
assert_eq!(classify_host_request(&req), HostRequestKind::Ping);
}
}