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 class CustomerSupportAgent < LittleGhost::Agent tools TicketStatusTool end run = CustomerSupportAgent.ask("What is happening with ticket SUP-481?") run.response
Use application context for authorization, never model-selected input:
class OrderStatusTool < LittleGhost::Tool description "Look up an order for the current account." input_schema type: "object", properties: { order_number: {type: "string"} }, required: ["order_number"], additionalProperties: false def call(input) Orders.status_for( actor_id: run.invocation.actor_id, account_id: run.invocation.context.fetch("account_id"), order_number: input.fetch("order_number") ) end end class OrderSupportAgent < LittleGhost::Agent tools OrderStatusTool end OrderSupportAgent.ask( "Where is order 481?", actor_id: authenticated_user.id, context: {account_id: authenticated_user.account_id} )
Each value comes from a different part of the run:
input-
Arguments selected by the model. The schema checks their shape, not their permission to perform an operation.
run.invocation.context-
Current request values supplied by the application. Use these for authorization after the application authenticates the caller.
context.state-
Mutable working state for the run. It may include values restored from a
Session, so check saved values again before trusting them. Tool::Binding-
Run-scoped objects such as the
Agent,Run,Runtime, workspace, and sandbox. TheBindingsuppliesrun; it does not contain model arguments.
The class DSL produces the specification sent to models. During an Agent run, the tool registry creates and binds one Tool instance. Tests and custom integrations may call execute directly; it validates the arguments, calls call, and returns a normalized internal result. Tool.define offers the same contract for an embedded implementation.
Mutable Tool instance state belongs to one Agent run. Registries close tool instances that implement close; exclusive true prevents that tool from overlapping other exclusive tools 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.
See the Tools guide for the complete path from model-selected input to application context, sandbox delegation, concurrency, and code mode.
Attributes
RunContext supplied to the current execute call, or nil outside execution.
Public Class Methods
Source
# File lib/little_ghost/tool.rb, line 279 def available?(binding) !availability_value || !!availability_value.call(binding) end
Returns whether this Tool should be registered for binding.
Source
# File lib/little_ghost/tool.rb, line 272 def available_if(&predicate) return availability_value unless predicate self.availability_value = predicate end
Declares whether this Tool is available for a run-scoped binding. With no block, returns the configured predicate or nil. ToolRegistry omits a Tool whose predicate returns false before constructing it.
# File lib/little_ghost/tool.rb, line 290 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 235 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 263 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 249 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 362 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 312 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 224 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 368 def agent = binding.agent
Bound agent, when the tool belongs to an agent run.
Source
# File lib/little_ghost/tool.rb, line 410 def call(_input) raise AbstractMethodError, "#{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 415 def close end
Releases resources owned by this tool. Subclasses may override it.
Source
# File lib/little_ghost/tool.rb, line 353 def description = self.class.description
Model-visible description declared by the tool class.
Source
# File lib/little_ghost/tool.rb, line 359 def exclusive? = self.class.exclusive
Indicates whether calls use the run-wide exclusive-tool lock.
# File lib/little_ghost/tool.rb, line 385 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 value = bound_for(context).call(input) return normalize_execution_result(value) if value.is_a?(ExecutionResult) return success(value.value, artifacts: value.artifacts) if value.is_a?(Result) success(value) rescue CancelledError, DeadlineExceededError, CleanupError raise rescue ToolError => error failure(error.message, error:) rescue => error failure("Tool failed (#{error.class})", error:) end
Source
# File lib/little_ghost/tool.rb, line 355 def input_schema = self.class.input_schema
Normalized JSON input schema declared by the tool class.
Source
# File lib/little_ghost/tool.rb, line 374 def model = binding.model
Bound model, when available.
Source
# File lib/little_ghost/tool.rb, line 370 def run = binding.run
Bound run, when available.
Source
# File lib/little_ghost/tool.rb, line 372 def runtime = binding.runtime
Bound runtime, when available.
Source
# File lib/little_ghost/tool.rb, line 378 def sandbox = binding.sandbox
Bound sandbox, when available.
Source
# File lib/little_ghost/tool.rb, line 357 def specification = self.class.specification
Frozen provider-facing tool specification.
Source
# File lib/little_ghost/tool.rb, line 351 def tool_name = self.class.tool_name
Model-visible name declared by the tool class.
Source
# File lib/little_ghost/tool.rb, line 376 def workspace = binding.workspace
Bound workspace, when available.