#![cfg(not(target_arch = "wasm32"))]
use crate::error::{Error, ErrorCode, Result};
use crate::server::roots::ListRootsResult;
use crate::types::elicitation::{ElicitRequestParams, ElicitResult};
use crate::types::sampling::{
CreateMessageParams, CreateMessageResult, CreateMessageResultWithTools,
};
use crate::types::ProgressToken;
use async_trait::async_trait;
#[async_trait]
pub trait PeerHandle: Send + Sync {
async fn sample(&self, params: CreateMessageParams) -> Result<CreateMessageResult>;
async fn sample_with_tools(
&self,
params: CreateMessageParams,
) -> Result<CreateMessageResultWithTools> {
let legacy = self.sample(params).await?;
Ok(CreateMessageResultWithTools::from_single(legacy))
}
async fn list_roots(&self) -> Result<ListRootsResult>;
async fn elicit(&self, params: ElicitRequestParams) -> Result<ElicitResult> {
let _ = params;
Err(Error::protocol(
ErrorCode::METHOD_NOT_FOUND,
"elicitation/create is unsupported by this peer handle: \
PeerHandle::elicit was never implemented, so no user was asked",
))
}
async fn progress_notify(
&self,
token: ProgressToken,
progress: f64,
total: Option<f64>,
message: Option<String>,
) -> Result<()>;
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::sync::Arc;
struct BarePeer;
#[async_trait]
impl PeerHandle for BarePeer {
async fn sample(&self, _params: CreateMessageParams) -> Result<CreateMessageResult> {
Err(Error::protocol(
ErrorCode::METHOD_NOT_FOUND,
"no sampling here",
))
}
async fn list_roots(&self) -> Result<ListRootsResult> {
Ok(ListRootsResult { roots: Vec::new() })
}
async fn progress_notify(
&self,
_token: ProgressToken,
_progress: f64,
_total: Option<f64>,
_message: Option<String>,
) -> Result<()> {
Ok(())
}
}
fn form_params() -> ElicitRequestParams {
ElicitRequestParams::Form {
message: "approve?".to_string(),
requested_schema: json!({ "type": "object" }),
}
}
#[tokio::test]
async fn bare_implementor_elicit_is_err_not_ok() {
let result = BarePeer.elicit(form_params()).await;
let error = match result {
Err(error) => error,
Ok(answer) => panic!(
"the default elicit body must NOT synthesize a user decision, got action {:?}",
answer.action
),
};
match error {
Error::Protocol {
code, ref message, ..
} => {
assert_eq!(code, ErrorCode::METHOD_NOT_FOUND);
assert!(
message.contains("elicitation/create"),
"the error must name the missing capability, got: {message}"
);
},
other => panic!("expected a protocol error, got: {other:?}"),
}
}
#[tokio::test]
async fn bare_implementor_elicit_is_err_through_dyn_peer_handle() {
let peer: Arc<dyn PeerHandle> = Arc::new(BarePeer);
assert!(
peer.elicit(form_params()).await.is_err(),
"the loud default must survive erasure to `dyn PeerHandle`"
);
}
}