pub struct ToolBuilder { /* private fields */ }Expand description
Builder for creating tools with a fluent API.
The ToolBuilder provides a convenient, readable way to construct tools
using method chaining. It’s especially useful when building tools incrementally
or when the schema structure is determined dynamically.
§Builder Pattern Benefits
- Readability: Method chains read like natural language
- Flexibility: Add parameters conditionally
- Type safety: Catches errors at compile time
- Discoverability: IDE autocomplete shows available options
§Workflow
- Create builder with
tool()orToolBuilder::new() - Add parameters with
.param() - Optionally set schema with
.schema() - Finalize with
.build()and provide handler
§Examples
See the tool() function for detailed examples.
§Note on Schema Mutation
If you call .schema() after .param(), the parameters will be replaced
by the new schema. Similarly, calling .param() after .schema() will
reset a non-object schema to an empty object before adding the parameter.
Generally, use either .schema() or .param(), not both.
Implementations§
Source§impl ToolBuilder
impl ToolBuilder
Sourcepub fn new(name: impl Into<String>, description: impl Into<String>) -> Self
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self
Start building a new tool with a name and description.
This creates a builder with an empty schema. You can then add parameters
using .param() or set a complete schema with
.schema().
§Parameters
name: Tool identifier (converted to String via Into trait)description: Human-readable explanation of what the tool does
§Examples
let builder = ToolBuilder::new("search", "Search for information");
// builder.param(...).build(...)Typically you’ll use the tool() convenience function instead of calling
this directly.
Sourcepub fn schema(self, schema: Value) -> Self
pub fn schema(self, schema: Value) -> Self
Set the complete input schema.
This replaces any schema or parameters set previously. Use this when you have a pre-built schema object (especially useful for complex schemas with nested structures).
§Schema Format
Accepts any of the formats supported by Tool::new:
- Simple type notation:
{"param": "string"} - Extended schema:
{"param": {"type": "string", "description": "..."}} - Full JSON Schema:
{"type": "object", "properties": {...}, "required": [...]}
§Warning
This overwrites any parameters added via .param(). Generally, choose
one approach: either use .param() for simple cases or .schema() for
complex cases, but not both.
§Examples
let my_tool = tool("api_call", "Make an API call")
.schema(json!({
"endpoint": {
"type": "string",
"description": "API endpoint URL",
"pattern": "^https://"
},
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"]
}
}))
.build(|_| async { Ok(json!({})) });Sourcepub fn param(self, name: &str, type_str: &str) -> Self
pub fn param(self, name: &str, type_str: &str) -> Self
Add a single parameter to the schema.
This is a convenience method for building schemas incrementally. Each call adds one parameter with a simple type string.
§Parameters
name: Parameter name (will be required in tool calls)type_str: Type string like “string”, “number”, “boolean”, etc. Supported types: “string”, “number”, “integer”, “boolean”, “array”, “object”.
§Behavior
- If the current schema is not an object (e.g., you called
.schema()with a non-object value), it will be reset to an empty object first. - All parameters added via
.param()are marked as required. - For optional parameters, use
.schema()with extended property format.
§Method Chaining
This method consumes self and returns it, enabling method chaining:
let my_tool = tool("calculate", "Perform calculation")
.param("operation", "string")
.param("x", "number")
.param("y", "number")
.build(|_| async { Ok(json!({})) });§Examples
// Add multiple parameters
let weather_tool = tool("get_weather", "Get weather for a location")
.param("location", "string")
.param("units", "string")
.build(|args| async move {
// Implementation
Ok(json!({"temp": 72}))
});Sourcepub fn build<F, Fut>(self, handler: F) -> Tool
pub fn build<F, Fut>(self, handler: F) -> Tool
Build the final Tool with a handler function.
This consumes the builder and produces a Tool ready for use. The handler
function defines what happens when the tool is called.
§Handler Requirements
The handler must be:
- An async function or closure
- Accept a single
Valueargument (the tool’s input parameters) - Return a
Future<Output = Result<Value>> - Implement
Send + Sync + 'staticfor thread safety
§Generic Parameters
F: The handler function type (inferred from the closure/function you provide)Fut: The future type returned by the handler (inferred automatically)
§Examples
§Simple Handler
let my_tool = tool("echo", "Echo back the input")
.param("message", "string")
.build(|args| async move {
Ok(args) // Echo arguments back
});§Handler with External State
let counter = Arc::new(std::sync::atomic::AtomicU32::new(0));
let my_tool = tool("increment", "Increment a counter")
.build(move |_args| {
let counter = counter.clone();
async move {
let val = counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
Ok(json!({"count": val + 1}))
}
});§Handler with Error Handling
let my_tool = tool("divide", "Divide two numbers")
.param("a", "number")
.param("b", "number")
.build(|args| async move {
let a = args["a"].as_f64().ok_or_else(|| Error::tool("Invalid 'a' parameter"))?;
let b = args["b"].as_f64().ok_or_else(|| Error::tool("Invalid 'b' parameter"))?;
if b == 0.0 {
return Err(Error::tool("Division by zero"));
}
Ok(json!({"result": a / b}))
});