Class: Tools::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/tools/registry.rb

Overview

Manages tool registration, schema export, and dispatch. Accepts both tool classes (e.g. Base subclasses) and tool instances (e.g. McpTool) via duck typing. Classes are instantiated with the registry’s context on each execution; instances are called directly since they carry their own state.

Examples:

registry = Tools::Registry.new(context: {shell_session: my_shell})
registry.register(Tools::Bash)
registry.execute("bash", {"command" => "ls"})

Constant Summary collapse

STANDARD_TOOLS =

Standard tools available to every session unless filtered out by Session#granted_tools.

[Tools::Bash, Tools::Read, Tools::Write, Tools::Edit, Tools::WebGet, Tools::Think, Tools::ViewMessages, Tools::SearchMessages].freeze
ALWAYS_GRANTED_TOOLS =

Tools that bypass Session#granted_tools filtering — the agent’s reasoning depends on them regardless of task scope.

[Tools::Think].freeze
STANDARD_TOOLS_BY_NAME =

Name-to-class mapping for granted-tools filtering.

STANDARD_TOOLS.index_by(&:tool_name).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context: {}) ⇒ Registry

Returns a new instance of Registry.

Parameters:

  • context (Hash) (defaults to: {})

    keyword arguments forwarded to every tool constructor



91
92
93
94
# File 'lib/tools/registry.rb', line 91

def initialize(context: {})
  @tools = {}
  @context = context
end

Instance Attribute Details

#toolsHash{String => Class, Object} (readonly)

Returns registered tools keyed by name.

Returns:

  • (Hash{String => Class, Object})

    registered tools keyed by name



88
89
90
# File 'lib/tools/registry.rb', line 88

def tools
  @tools
end

Class Method Details

.build(session:, shell_session:) ⇒ Registry

Builds a registry appropriate for the given session: standard tools filtered through Session#granted_tools, plus spawn tools for main sessions or mark_goal_completed for sub-agents, plus any tools exposed by configured MCP servers.

MCP registration warnings are emitted as system messages so both the user (in verbose mode) and the LLM see them.

Parameters:

  • session (Session)

    the session requesting tools

  • shell_session (ShellSession)

    persistent PTY for Bash-family tools

Returns:



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/tools/registry.rb', line 40

def build(session:, shell_session:)
  registry = new(context: {shell_session: shell_session, session: session})

  tool_classes_for(session).each { |tool| registry.register(tool) }

  Mcp::ClientManager.shared.register_tools(registry).each do |message|
    Events::Bus.emit(Events::SystemMessage.new(content: message, session_id: session.id))
  end

  registry
end

.tool_classes_for(session) ⇒ Array<Class<Tools::Base>>

Resolves the ordered tool-class list for a session: granted standard tools (or all of them when granted_tools is nil), plus spawn tools for main sessions or mark_goal_completed for sub-agents. MCP tools are excluded — they are dynamic and registered separately by build. Single source of truth for build, Session#tool_schemas, and the system-prompt section assemblers.

Parameters:

Returns:



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/tools/registry.rb', line 61

def tool_classes_for(session)
  tools = granted_standard_tools(session).dup

  if session.sub_agent?
    tools.push(Tools::MarkGoalCompleted)
  else
    tools.push(Tools::SpawnSubagent, Tools::SpawnSpecialist, Tools::OpenIssue)
  end

  tools
end

Instance Method Details

#any?Boolean

Returns whether any tools are registered.

Returns:

  • (Boolean)

    whether any tools are registered



161
162
163
# File 'lib/tools/registry.rb', line 161

def any?
  @tools.any?
end

#execute(name, input, tool_use_id: nil) ⇒ String, Hash

Execute a tool by name. Classes are instantiated with the registry’s context; instances are called directly. The enclosing tool_use_id is merged into the context when provided so tools that need to reference their own invoking tool_call (e.g. SpawnSubagent persisting spawn_tool_use_id on the child session) can read it via a named kwarg in their initializer.

Parameters:

  • name (String)

    registered tool name

  • input (Hash)

    tool input parameters (may include “timeout” for tools that support per-call timeout overrides)

  • tool_use_id (String, nil) (defaults to: nil)

    the invoking tool_call’s pairing id

Returns:

  • (String, Hash)

    tool execution result

Raises:



134
135
136
137
138
139
# File 'lib/tools/registry.rb', line 134

def execute(name, input, tool_use_id: nil)
  tool = @tools.fetch(name) { raise UnknownToolError, "Unknown tool: #{name}" }
  context = tool_use_id ? @context.merge(tool_use_id: tool_use_id) : @context
  instance = tool.is_a?(Class) ? tool.new(**context) : tool
  instance.execute(input)
end

#register(tool) ⇒ void

This method returns an undefined value.

Register a tool class or instance. Must respond to tool_name and schema.

Parameters:

  • tool (Class<Tools::Base>, #tool_name)

    tool class or duck-typed instance



99
100
101
# File 'lib/tools/registry.rb', line 99

def register(tool)
  @tools[tool.tool_name] = tool
end

#registered?(name) ⇒ Boolean

Returns whether a tool with the given name is registered.

Parameters:

  • name (String)

    tool name to check

Returns:

  • (Boolean)

    whether a tool with the given name is registered



156
157
158
# File 'lib/tools/registry.rb', line 156

def registered?(name)
  @tools.key?(name)
end

#schemasArray<Hash>

Returns tool schemas for the Anthropic API. The last schema is annotated with cache_control so the API caches the entire tools prefix (tools are evaluated first in cache prefix order).

Returns:

  • (Array<Hash>)

    schema array for the Anthropic tools API parameter. Each schema includes an optional ‘timeout` parameter (seconds) injected by the registry. The agent can override the default per call for long-running operations. Tools with session-dependent schemas (e.g. Think with budget-based maxLength, Bash with CWD in description) are instantiated with context to generate their schema:

    • Think: budget-based maxLength

    • Bash: CWD embedded in description



114
115
116
117
118
119
# File 'lib/tools/registry.rb', line 114

def schemas
  default = Anima::Settings.tool_timeout
  result = @tools.values.map { |tool| inject_timeout(resolve_schema(tool), default) }
  result.last[:cache_control] = {type: "ephemeral"}
  result
end

#truncation_threshold(name) ⇒ Integer?

Returns the truncation threshold for a tool, or nil if the tool opts out of truncation (e.g. read_file tool has its own pagination). MCP tools and other duck-typed instances use the default threshold.

Parameters:

  • name (String)

    registered tool name

Returns:

  • (Integer, nil)

    character threshold, or nil to skip truncation



147
148
149
150
151
152
# File 'lib/tools/registry.rb', line 147

def truncation_threshold(name)
  tool = @tools[name]
  return tool.truncation_threshold if tool&.respond_to?(:truncation_threshold)

  Anima::Settings.max_tool_response_chars
end