elicitation 0.8.0

Conversational elicitation of strongly-typed Rust values via MCP
Documentation
//! String type implementation.

use crate::{ElicitCommunicator, ElicitResult, Elicitation, Prompt, Select};

/// Elicitation style variants for String.
///
/// Demonstrates Agent vs Human prompting strategies:
/// - Agent: Terse, assumes technical context
/// - Human: Friendly, more explanatory
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum StringStyle {
    /// Human-friendly prompting (default).
    #[default]
    Human,
    /// Terse agent-oriented prompting.
    Agent,
}

impl Prompt for StringStyle {
    fn prompt() -> Option<&'static str> {
        Some("Select elicitation style:")
    }
}

impl Select for StringStyle {
    fn options() -> Vec<Self> {
        vec![Self::Human, Self::Agent]
    }

    fn labels() -> Vec<String> {
        vec!["human".to_string(), "agent".to_string()]
    }

    fn from_label(label: &str) -> Option<Self> {
        match label {
            "human" => Some(Self::Human),
            "agent" => Some(Self::Agent),
            _ => None,
        }
    }
}

impl Elicitation for StringStyle {
    type Style = ();

    async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
        let prompt = <Self as Prompt>::prompt().unwrap();
        let labels = <Self as Select>::labels();

        // Use Select paradigm with send_prompt (for server-side)
        let formatted_prompt = format!("{}\nOptions: {}", prompt, labels.join(", "));
        let response = communicator.send_prompt(&formatted_prompt).await?;

        <Self as Select>::from_label(response.trim()).ok_or_else(|| {
            crate::ElicitError::from(crate::ElicitErrorKind::ParseError(format!(
                "Invalid style selection: {}",
                response.trim()
            )))
        })
    }
}

impl Prompt for String {
    fn prompt() -> Option<&'static str> {
        Some("Please enter text:")
    }
}

impl Elicitation for String {
    type Style = StringStyle;

    #[tracing::instrument(skip(communicator))]
    async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
        let style = communicator.style_or_elicit::<Self>().await?;

        tracing::debug!(?style, "Eliciting String with style");

        // Style-specific prompting
        let prompt = match style {
            StringStyle::Human => "Please provide a text value:",
            StringStyle::Agent => "Value?",
        };

        // Elicit directly with custom prompt
        communicator.send_prompt(prompt).await
    }
}