Skip to main content

mcp_plugin_api/
tool.rs

1//! Type-safe tool definitions
2//!
3//! This module provides a high-level API for defining tools with
4//! compile-time type checking and automatic JSON schema generation.
5
6use serde_json::{json, Value};
7
8/// A parameter definition for a tool
9#[derive(Debug, Clone)]
10pub struct ToolParam {
11    pub name: String,
12    pub description: String,
13    pub param_type: ParamType,
14    pub required: bool,
15}
16
17/// Parameter type enumeration
18#[derive(Debug, Clone)]
19pub enum ParamType {
20    String,
21    Integer,
22    Number,
23    Boolean,
24    Object,
25    Array,
26}
27
28impl ParamType {
29    /// Convert to JSON Schema type string
30    pub fn to_json_type(&self) -> &'static str {
31        match self {
32            ParamType::String => "string",
33            ParamType::Integer => "integer",
34            ParamType::Number => "number",
35            ParamType::Boolean => "boolean",
36            ParamType::Object => "object",
37            ParamType::Array => "array",
38        }
39    }
40}
41
42/// Tool handler function type
43///
44/// A tool handler takes JSON arguments and returns either a JSON result
45/// or an error message.
46pub type ToolHandler = fn(&Value) -> Result<Value, String>;
47
48/// A tool definition
49///
50/// This represents a single tool with its metadata and handler function.
51#[derive(Debug, Clone)]
52pub struct Tool {
53    pub active: bool,
54    pub name: String,
55    pub description: String,
56    pub params: Vec<ToolParam>,
57    pub handler: ToolHandler,
58}
59
60impl Tool {
61    /// Create a new tool definition with a builder
62    ///
63    /// # Example
64    ///
65    /// ```ignore
66    /// Tool::new("get_price", "Get the price of a product")
67    ///     .param_i64("product_id", "The product ID", true)
68    ///     .handler(handle_get_price)
69    /// ```
70    pub fn builder(name: &str, description: &str, active: bool) -> ToolBuilder {
71        ToolBuilder {
72            active,
73            name: name.to_string(),
74            description: description.to_string(),
75            params: Vec::new(),
76        }
77    }
78    
79    /// Convert tool definition to JSON Schema format
80    ///
81    /// Returns a JSON object compatible with MCP protocol:
82    /// ```json
83    /// {
84    ///   "name": "tool_name",
85    ///   "description": "Tool description",
86    ///   "inputSchema": {
87    ///     "type": "object",
88    ///     "properties": { ... },
89    ///     "required": [ ... ]
90    ///   }
91    /// }
92    /// ```
93    pub fn to_json_schema(&self) -> Value {
94        let mut properties = serde_json::Map::new();
95        let mut required = Vec::new();
96        
97        for param in &self.params {
98            properties.insert(
99                param.name.clone(),
100                json!({
101                    "type": param.param_type.to_json_type(),
102                    "description": param.description
103                })
104            );
105            
106            if param.required {
107                required.push(param.name.clone());
108            }
109        }
110        
111        json!({
112            "name": self.name,
113            "description": self.description,
114            "inputSchema": {
115                "type": "object",
116                "properties": properties,
117                "required": required
118            }
119        })
120    }
121}
122
123/// Builder for creating tools with a fluent API
124pub struct ToolBuilder {
125    active: bool,
126    name: String,
127    description: String,
128    params: Vec<ToolParam>,
129}
130
131impl ToolBuilder {
132    /// Add a string parameter
133    ///
134    /// # Arguments
135    /// * `name` - Parameter name
136    /// * `description` - Parameter description
137    /// * `required` - Whether the parameter is required
138    pub fn param_string(mut self, name: &str, description: &str, required: bool) -> Self {
139        self.params.push(ToolParam {
140            name: name.to_string(),
141            description: description.to_string(),
142            param_type: ParamType::String,
143            required,
144        });
145        self
146    }
147    
148    /// Add an integer parameter (i64)
149    pub fn param_i64(mut self, name: &str, description: &str, required: bool) -> Self {
150        self.params.push(ToolParam {
151            name: name.to_string(),
152            description: description.to_string(),
153            param_type: ParamType::Integer,
154            required,
155        });
156        self
157    }
158    
159    /// Add a number parameter (f64)
160    pub fn param_f64(mut self, name: &str, description: &str, required: bool) -> Self {
161        self.params.push(ToolParam {
162            name: name.to_string(),
163            description: description.to_string(),
164            param_type: ParamType::Number,
165            required,
166        });
167        self
168    }
169    
170    /// Add a boolean parameter
171    pub fn param_bool(mut self, name: &str, description: &str, required: bool) -> Self {
172        self.params.push(ToolParam {
173            name: name.to_string(),
174            description: description.to_string(),
175            param_type: ParamType::Boolean,
176            required,
177        });
178        self
179    }
180    
181    /// Add an object parameter
182    pub fn param_object(mut self, name: &str, description: &str, required: bool) -> Self {
183        self.params.push(ToolParam {
184            name: name.to_string(),
185            description: description.to_string(),
186            param_type: ParamType::Object,
187            required,
188        });
189        self
190    }
191    
192    /// Add an array parameter
193    pub fn param_array(mut self, name: &str, description: &str, required: bool) -> Self {
194        self.params.push(ToolParam {
195            name: name.to_string(),
196            description: description.to_string(),
197            param_type: ParamType::Array,
198            required,
199        });
200        self
201    }
202    
203    /// Set the handler function and finalize the tool
204    ///
205    /// This consumes the builder and returns the completed Tool.
206    pub fn handler(self, handler: ToolHandler) -> Tool {
207        Tool {
208            active: self.active,
209            name: self.name,
210            description: self.description,
211            params: self.params,
212            handler,
213        }
214    }
215}
216