elicitation 0.8.0

Conversational elicitation of strongly-typed Rust values via MCP
Documentation
//! Automatic tool discovery and registration via inventory.
//!
//! This module provides infrastructure for automatically collecting all
//! `elicit_checked()` methods generated by `#[derive(Elicit)]` at runtime.
//!
//! # How It Works
//!
//! 1. Each `#[derive(Elicit)]` generates an `elicit_checked()` method
//! 2. The derive macro also submits a descriptor to the inventory
//! 3. At runtime, call `collect_all_elicit_tools()` to get all registered tools
//!
//! # Example
//!
//! ```ignore
//! use elicitation::{Elicit, collect_all_elicit_tools};
//!
//! #[derive(Elicit)]
//! struct Config { timeout: u32 }
//!
//! #[derive(Elicit)]
//! struct User { name: String }
//!
//! // Automatically collect all elicit tools
//! let tools = collect_all_elicit_tools();
//! println!("Found {} elicit tools", tools.len());
//! ```

/// Describes an elicit tool that can be registered with MCP.
///
/// Each type with `#[derive(Elicit)]` generates one of these descriptors
/// and submits it to the inventory at compile time.
#[derive(Debug, Clone)]
pub struct ElicitToolDescriptor {
    /// The name of the type (e.g., "Config", "User")
    pub type_name: &'static str,

    /// The module path where the type is defined
    pub module_path: &'static str,
}

impl ElicitToolDescriptor {
    /// Create a new tool descriptor.
    #[doc(hidden)]
    pub const fn new(type_name: &'static str, module_path: &'static str) -> Self {
        Self {
            type_name,
            module_path,
        }
    }

    /// Get the fully qualified name of this tool.
    ///
    /// Returns `module::Type` format.
    pub fn qualified_name(&self) -> String {
        format!("{}::{}", self.module_path, self.type_name)
    }
}

inventory::collect!(ElicitToolDescriptor);

/// Collect all elicit tools registered via `#[derive(Elicit)]`.
///
/// Returns a vector of descriptors for all types that have derived `Elicit`
/// in the current binary. This enables automatic tool discovery without
/// manual registration.
///
/// # Example
///
/// ```ignore
/// use elicitation::collect_all_elicit_tools;
///
/// let tools = collect_all_elicit_tools();
/// for tool in tools {
///     println!("Found tool: {}", tool.qualified_name());
/// }
/// ```
pub fn collect_all_elicit_tools() -> Vec<&'static ElicitToolDescriptor> {
    inventory::iter::<ElicitToolDescriptor>().collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tool_descriptor() {
        let descriptor = ElicitToolDescriptor::new("Config", "my_app::models");
        assert_eq!(descriptor.type_name, "Config");
        assert_eq!(descriptor.module_path, "my_app::models");
        assert_eq!(descriptor.qualified_name(), "my_app::models::Config");
    }
}