Salvor tools: the typed tool-contract layer for the agent runtime.
A tool is a typed, effect-classified operation a model may call. This crate defines what a tool is and how the runtime dispatches one; it declares contracts and performs no IO. Persisting tool-call events and driving the dispatch is the runtime's job, built on the seam this crate exposes.
The layers
- The typed contract. [
ToolMeta] carries a tool's identity andEffect; [ToolHandler] adds its typedInput,Output, and asynccall. The split is the seam the future#[derive(Tool)]macro cuts: the macro generatesToolMeta, the user writesToolHandler. See [ToolMeta]'s docs for the exact contract. - The tool outcome. [
ToolHandler::call] returns a [ToolOutcome]: either anOutput, or a [Suspension] that parks the run for a human. Suspension is a return value in v0.1, not a runtime call. - Type-erased dispatch. [
DynTool] is theValue-in/Value-out, dyn-compatible trait the runtime dispatches through. [TypedTool] adapts any [ToolHandler] into aDynTool, validating the model's JSON against the input type before the handler runs. MCP-backed tools (a later task) implementDynTooldirectly. - The registry. [
ToolSet] registers tools by name, looks them up, and enumerates them as [ToolDescriptor]s for a model. Duplicate names are a [RegistryError]. - Retry policy. [
RetryPolicy] encodes the per-effect rule for retrying a failed live execution. It classifies; the runtime loop enforces. - MCP tools. Behind the
mcpcargo feature (on by default), the [mcp] module connects to an MCP server over stdio and surfaces each of its tools as a [DynTool], registering alongside native tools. All of the MCP dependency surface (the rmcp SDK, a Tokio runtime) is gated behind that feature, so the contract layer above still builds with--no-default-features. MCP stays isolated to that one module by design: rmcp/MCP protocol churn is a standing risk.
Errors
A tool's own failure is a [HandlerError]. The erased layer's error is a
[ToolError], whose InvalidInput variant (the
model sent malformed arguments, and the handler never ran) is deliberately
distinct from Handler (the tool ran and failed), so
the runtime loop can route them differently.