class LittleGhost::Tool
Give an agent a validated way to call application code. Every tool declares a model-visible name, description, and input shape before implementing its operation.
class TicketStatusTool < LittleGhost::Tool tool_name "ticket_status" description "Look up a support ticket's status." input_schema type: "object", properties: { ticket_id: {type: "string"} }, required: ["ticket_id"], additionalProperties: false def call(input) {ticket_id: input.fetch("ticket_id"), status: "waiting_on_customer"} end end result = TicketStatusTool.new.execute("ticket_id" => "SUP-481") result.success? # => true JSON.parse(result.content) # => {"ticket_id"=>"SUP-481", "status"=>"waiting_on_customer"}
The class DSL produces the frozen specification sent to models. execute validates incoming arguments, invokes call, and normalizes strings, JSON-compatible collections, and other return values to model-facing text. Tool.define offers the same contract for an embedded implementation.
A tool registry creates one instance per agent run and supplies a Binding for access to the agent, run, runtime, model, workspace, and sandbox. Mutable per-instance state therefore belongs to that run. Registries close tools that implement close; exclusive true serializes calls against every other exclusive tool in the same run.
Validation and application ToolError failures become error results. A ToolError message is visible to the model and must be safe to disclose; unexpected exception messages are replaced with their class name. Cancellation, deadlines, and cleanup errors propagate instead of becoming ordinary tool output. The configured sandbox, not Tool itself, enforces filesystem and process isolation.
Attributes
RunContext supplied to the current execute call, or nil outside execution.
Public Class Methods
# File lib/little_ghost/tool.rb, line 192 def define(name:, description:, input_schema: {}, &implementation) raise ArgumentError, "A tool implementation block is required" unless implementation Class.new(self) do tool_name(name) description(description) input_schema(input_schema) define_method(:call) do |input| accepts_context = implementation.parameters.any? do |kind, parameter| kind == :keyrest || (%i[key keyreq].include?(kind) && parameter == :context) end if accepts_context implementation.call(input, context: context) else implementation.call(input) end end end end
Creates an anonymous Tool subclass backed by implementation. The block receives input and may also accept the context: keyword.
tool = LittleGhost::Tool.define( name: "echo", description: "Echo text.", input_schema: {type: "object"} ) { |input| input.fetch("text") }
# File lib/little_ghost/tool.rb, line 151 def description(*values) return description_value if values.empty? self.description_value = String(values.fetch(0)).freeze end
The model-visible description used to decide when the tool applies.
# File lib/little_ghost/tool.rb, line 179 def exclusive(*values) return !!exclusive_value if values.empty? self.exclusive_value = !!values.fetch(0) end
Whether calls acquire the run-wide exclusive tool lock.
# File lib/little_ghost/tool.rb, line 165 def input_schema(*values) return input_schema_value || {}.freeze if values.empty? value = values.fetch(0) raise ArgumentError, "input_schema must be a hash" unless value.is_a?(Hash) self.input_schema_value = deep_freeze(value) end
The frozen JSON Schema subset used to validate model input.
Setting a non-Hash schema raises ArgumentError. Keys are normalized to strings and the entire value is deeply frozen.
Source
# File lib/little_ghost/tool.rb, line 264 def initialize(binding: Binding.new) @binding = binding @state = {} end
Creates a tool with the run-scoped collaborators in binding.
Source
# File lib/little_ghost/tool.rb, line 214 def specification { name: tool_name, description: description, input_schema: input_schema }.freeze end
The frozen model-facing name, description, and input schema.
# File lib/little_ghost/tool.rb, line 140 def tool_name(*values) return configured_name if values.empty? self.tool_name_value = String(values.fetch(0)).freeze end
The model-visible tool name.
Named classes derive a snake-cased default; passing value replaces it.
Public Instance Methods
Source
# File lib/little_ghost/tool.rb, line 270 def agent = binding.agent
Bound agent, when the tool belongs to an agent run.
Source
# File lib/little_ghost/tool.rb, line 308 def call(_input) raise NotImplementedError, "#{self.class} must implement #call" end
Implements the model-requested operation.
Subclasses must override this method. The current RunContext is available through context while the call executes.
Source
# File lib/little_ghost/tool.rb, line 255 def description = self.class.description
Model-visible description declared by the tool class.
Source
# File lib/little_ghost/tool.rb, line 261 def exclusive? = self.class.exclusive
Indicates whether calls use the run-wide exclusive-tool lock.
# File lib/little_ghost/tool.rb, line 287 def execute(input, context: RunContext.new) context ||= RunContext.new errors = SchemaValidator.new(self.class.input_schema).validate(input) unless errors.empty? message = "Invalid tool input: #{errors.join("; ")}" return failure(message, error: ToolError.new(message)) end success(sanitize(bound_for(context).call(input))) rescue CancelledError, DeadlineExceededError, CleanupError raise rescue ToolError => error failure(error.message, error:) rescue => error failure("Tool failed (#{error.class})", error:) end
Validates input and invokes the tool, returning an ExecutionResult.
Cancellation, deadline, and cleanup exceptions remain control-flow exceptions. ToolError and unexpected failures become sanitized error results; unexpected exception messages are not exposed to the model.
Source
# File lib/little_ghost/tool.rb, line 257 def input_schema = self.class.input_schema
Normalized JSON input schema declared by the tool class.
Source
# File lib/little_ghost/tool.rb, line 276 def model = binding.model
Bound model, when available.
Source
# File lib/little_ghost/tool.rb, line 272 def run = binding.run
Bound run, when available.
Source
# File lib/little_ghost/tool.rb, line 274 def runtime = binding.runtime
Bound runtime, when available.
Source
# File lib/little_ghost/tool.rb, line 280 def sandbox = binding.sandbox
Bound sandbox, when available.
Source
# File lib/little_ghost/tool.rb, line 259 def specification = self.class.specification
Frozen provider-facing tool specification.
Source
# File lib/little_ghost/tool.rb, line 253 def tool_name = self.class.tool_name
Model-visible name declared by the tool class.
Source
# File lib/little_ghost/tool.rb, line 278 def workspace = binding.workspace
Bound workspace, when available.