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.
51pub struct Tool {
52    pub active: bool,
53    pub name: String,
54    pub description: String,
55    pub params: Vec<ToolParam>,
56    pub handler: ToolHandler,
57}
58
59impl Tool {
60    /// Create a new tool definition with a builder
61    ///
62    /// # Example
63    ///
64    /// ```ignore
65    /// Tool::new("get_price", "Get the price of a product")
66    ///     .param_i64("product_id", "The product ID", true)
67    ///     .handler(handle_get_price)
68    /// ```
69    pub fn builder(name: &str, description: &str, active: bool) -> ToolBuilder {
70        ToolBuilder {
71            active,
72            name: name.to_string(),
73            description: description.to_string(),
74            params: Vec::new(),
75        }
76    }
77    
78    /// Convert tool definition to JSON Schema format
79    ///
80    /// Returns a JSON object compatible with MCP protocol:
81    /// ```json
82    /// {
83    ///   "name": "tool_name",
84    ///   "description": "Tool description",
85    ///   "inputSchema": {
86    ///     "type": "object",
87    ///     "properties": { ... },
88    ///     "required": [ ... ]
89    ///   }
90    /// }
91    /// ```
92    pub fn to_json_schema(&self) -> Value {
93        let mut properties = serde_json::Map::new();
94        let mut required = Vec::new();
95        
96        for param in &self.params {
97            properties.insert(
98                param.name.clone(),
99                json!({
100                    "type": param.param_type.to_json_type(),
101                    "description": param.description
102                })
103            );
104            
105            if param.required {
106                required.push(param.name.clone());
107            }
108        }
109        
110        json!({
111            "name": self.name,
112            "description": self.description,
113            "inputSchema": {
114                "type": "object",
115                "properties": properties,
116                "required": required
117            }
118        })
119    }
120}
121
122/// Builder for creating tools with a fluent API
123pub struct ToolBuilder {
124    active: bool,
125    name: String,
126    description: String,
127    params: Vec<ToolParam>,
128}
129
130impl ToolBuilder {
131    /// Add a string parameter
132    ///
133    /// # Arguments
134    /// * `name` - Parameter name
135    /// * `description` - Parameter description
136    /// * `required` - Whether the parameter is required
137    pub fn param_string(mut self, name: &str, description: &str, required: bool) -> Self {
138        self.params.push(ToolParam {
139            name: name.to_string(),
140            description: description.to_string(),
141            param_type: ParamType::String,
142            required,
143        });
144        self
145    }
146    
147    /// Add an integer parameter (i64)
148    pub fn param_i64(mut self, name: &str, description: &str, required: bool) -> Self {
149        self.params.push(ToolParam {
150            name: name.to_string(),
151            description: description.to_string(),
152            param_type: ParamType::Integer,
153            required,
154        });
155        self
156    }
157    
158    /// Add a number parameter (f64)
159    pub fn param_f64(mut self, name: &str, description: &str, required: bool) -> Self {
160        self.params.push(ToolParam {
161            name: name.to_string(),
162            description: description.to_string(),
163            param_type: ParamType::Number,
164            required,
165        });
166        self
167    }
168    
169    /// Add a boolean parameter
170    pub fn param_bool(mut self, name: &str, description: &str, required: bool) -> Self {
171        self.params.push(ToolParam {
172            name: name.to_string(),
173            description: description.to_string(),
174            param_type: ParamType::Boolean,
175            required,
176        });
177        self
178    }
179    
180    /// Add an object parameter
181    pub fn param_object(mut self, name: &str, description: &str, required: bool) -> Self {
182        self.params.push(ToolParam {
183            name: name.to_string(),
184            description: description.to_string(),
185            param_type: ParamType::Object,
186            required,
187        });
188        self
189    }
190    
191    /// Add an array parameter
192    pub fn param_array(mut self, name: &str, description: &str, required: bool) -> Self {
193        self.params.push(ToolParam {
194            name: name.to_string(),
195            description: description.to_string(),
196            param_type: ParamType::Array,
197            required,
198        });
199        self
200    }
201    
202    /// Set the handler function and finalize the tool
203    ///
204    /// This consumes the builder and returns the completed Tool.
205    pub fn handler(self, handler: ToolHandler) -> Tool {
206        Tool {
207            active: self.active,
208            name: self.name,
209            description: self.description,
210            params: self.params,
211            handler,
212        }
213    }
214}
215