kowalski-core 1.2.0

Kowalski Core Module: A Rust-based agent for interacting with Ollama models
Documentation
use crate::error::KowalskiError;
use crate::mcp::hub::McpHub;
use crate::mcp::types::McpToolDescription;
use crate::tools::{ParameterType, Tool, ToolInput, ToolOutput, ToolParameter};
use async_trait::async_trait;
use serde_json::Value;
use std::sync::Arc;

pub struct McpToolProxy {
    hub: Arc<McpHub>,
    name: String,
    description: String,
    parameters: Vec<ToolParameter>,
}

impl McpToolProxy {
    pub fn new(hub: Arc<McpHub>, display_name: String, description: McpToolDescription) -> Self {
        let parameters = Self::schema_to_parameters(&description.input_schema);
        Self {
            hub,
            name: display_name,
            description: description.description,
            parameters,
        }
    }

    fn schema_to_parameters(schema: &Value) -> Vec<ToolParameter> {
        let mut params = Vec::new();
        let required = schema
            .get("required")
            .and_then(|v| v.as_array())
            .map(|arr| {
                arr.iter()
                    .filter_map(|v| v.as_str().map(|s| s.to_string()))
                    .collect::<Vec<_>>()
            })
            .unwrap_or_default();

        if let Some(props) = schema.get("properties").and_then(|v| v.as_object()) {
            for (name, details) in props {
                let description = details
                    .get("description")
                    .and_then(|v| v.as_str())
                    .unwrap_or("No description provided")
                    .to_string();
                let required_flag = required.iter().any(|req| req == name);
                let default_value = details.get("default").map(|v| v.to_string());
                let param_type = details
                    .get("type")
                    .and_then(|v| v.as_str())
                    .map(|s| match s {
                        "number" => ParameterType::Number,
                        "boolean" => ParameterType::Boolean,
                        "array" => ParameterType::Array,
                        "object" => ParameterType::Object,
                        _ => ParameterType::String,
                    })
                    .unwrap_or(ParameterType::String);

                params.push(ToolParameter {
                    name: name.clone(),
                    description,
                    required: required_flag,
                    default_value,
                    parameter_type: param_type,
                });
            }
        }

        params
    }
}

#[async_trait]
impl Tool for McpToolProxy {
    async fn execute(&mut self, input: ToolInput) -> Result<ToolOutput, KowalskiError> {
        self.validate_input(&input)?;
        let value = self.hub.call_tool(&self.name, &input.parameters).await?;
        Ok(ToolOutput::new(value, None))
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn description(&self) -> &str {
        &self.description
    }

    fn parameters(&self) -> Vec<ToolParameter> {
        self.parameters.clone()
    }
}