Class: ToolForge::ToolDefinition

Inherits:
Object
  • Object
show all
Defined in:
lib/tool_forge/tool_definition.rb

Overview

ToolDefinition is the core class for defining tools that can be converted to both RubyLLM and MCP tool formats. It provides a clean DSL for defining tool metadata, parameters, helper methods, and execution logic.

Examples:

Basic tool definition

tool = ToolForge::ToolDefinition.new(:greet_user) do
  description 'Greets a user by name'
  param :name, type: :string, description: 'User name'
  execute { |name:| "Hello, #{name}!" }
end

Tool with helper methods

tool = ToolForge::ToolDefinition.new(:file_processor) do
  description 'Processes files with helper methods'
  param :file_path, type: :string

  # Instance helper method
  helper(:format_data) { |data| "FORMATTED: #{data}" }

  # Class helper method (useful for utilities like tar operations)
  class_helper(:add_to_tar) { |file, path| "Added #{file} to #{path}" }

  execute do |file_path:|
    data = File.read(file_path)
    formatted = format_data(data)
    tar_result = self.class.add_to_tar(file_path, '/archive')
    "#{formatted} - #{tar_result}"
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) { ... } ⇒ ToolDefinition

Creates a new tool definition with the given name.

Examples:

tool = ToolDefinition.new(:my_tool) do
  description 'A sample tool'
  param :input, type: :string
  execute { |input:| "Processed: #{input}" }
end

Parameters:

  • name (Symbol)

    the name of the tool

Yields:

  • optional block for configuring the tool using the DSL



51
52
53
54
55
56
57
58
59
# File 'lib/tool_forge/tool_definition.rb', line 51

def initialize(name, &)
  @name = name
  @description = nil
  @params = []
  @execute_block = nil
  @helper_methods = { instance: {}, class: {} }

  instance_eval(&) if block_given?
end

Instance Attribute Details

#execute_blockSymbol, ... (readonly)

Returns:

  • (Symbol)

    the name of the tool

  • (Array<Hash>)

    the parameters defined for the tool

  • (Proc)

    the execution block for the tool

  • (Hash)

    the helper methods organized by type (:instance and :class)



38
39
40
# File 'lib/tool_forge/tool_definition.rb', line 38

def execute_block
  @execute_block
end

#helper_methodsSymbol, ... (readonly)

Returns:

  • (Symbol)

    the name of the tool

  • (Array<Hash>)

    the parameters defined for the tool

  • (Proc)

    the execution block for the tool

  • (Hash)

    the helper methods organized by type (:instance and :class)



38
39
40
# File 'lib/tool_forge/tool_definition.rb', line 38

def helper_methods
  @helper_methods
end

#nameSymbol, ... (readonly)

Returns:

  • (Symbol)

    the name of the tool

  • (Array<Hash>)

    the parameters defined for the tool

  • (Proc)

    the execution block for the tool

  • (Hash)

    the helper methods organized by type (:instance and :class)



38
39
40
# File 'lib/tool_forge/tool_definition.rb', line 38

def name
  @name
end

#paramsSymbol, ... (readonly)

Returns:

  • (Symbol)

    the name of the tool

  • (Array<Hash>)

    the parameters defined for the tool

  • (Proc)

    the execution block for the tool

  • (Hash)

    the helper methods organized by type (:instance and :class)



38
39
40
# File 'lib/tool_forge/tool_definition.rb', line 38

def params
  @params
end

Instance Method Details

#class_helper(method_name) {|*args| ... } ⇒ void

This method returns an undefined value.

Defines a class helper method that can be called within the execute block. Class helper methods are useful for utility functions that don’t depend on instance state. They are accessed via self.class.method_name in the execution context.

Examples:

class_helper(:add_to_tar) do |file_path, tar_path|
  # Implementation for adding files to tar archive
  "Added #{file_path} to tar as #{tar_path}"
end

execute do |file:|
  self.class.add_to_tar(file, '/archive/file.tar') # Called as class method
end

Parameters:

  • method_name (Symbol)

    the name of the class helper method

Yields:

  • (*args)

    block that defines the helper method logic



152
153
154
# File 'lib/tool_forge/tool_definition.rb', line 152

def class_helper(method_name, &block)
  @helper_methods[:class][method_name] = block
end

#description(text = nil) ⇒ String?

Sets or returns the tool description.

Examples:

Setting description

description 'This tool processes files'

Getting description

tool.description #=> 'This tool processes files'

Parameters:

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

    the description text to set, or nil to return current description

Returns:

  • (String, nil)

    the current description when called without arguments



71
72
73
74
75
76
77
# File 'lib/tool_forge/tool_definition.rb', line 71

def description(text = nil)
  if text
    @description = text
  else
    @description
  end
end

#execute {|**args| ... } ⇒ void

This method returns an undefined value.

Defines the execution logic for the tool.

Examples:

execute do |filename:, format:|
  data = File.read(filename)
  format == 'json' ? JSON.parse(data) : data
end

Yields:

  • (**args)

    block that receives tool parameters as keyword arguments



112
113
114
# File 'lib/tool_forge/tool_definition.rb', line 112

def execute(&block)
  @execute_block = block
end

#helper(method_name) {|*args| ... } ⇒ void

This method returns an undefined value.

Defines an instance helper method that can be called within the execute block. Instance helper methods are available as regular method calls in the execution context.

Examples:

helper(:format_output) do |data|
  "FORMATTED: #{data.upcase}"
end

execute do |input:|
  format_output(input) # Called as instance method
end

Parameters:

  • method_name (Symbol)

    the name of the helper method

Yields:

  • (*args)

    block that defines the helper method logic



131
132
133
# File 'lib/tool_forge/tool_definition.rb', line 131

def helper(method_name, &block)
  @helper_methods[:instance][method_name] = block
end

#param(name, type: :string, description: nil, required: true, default: nil) ⇒ Object

Defines a parameter for the tool.

Examples:

Required string parameter

param :filename, type: :string, description: 'File to process'

Optional parameter with default

param :format, type: :string, description: 'Output format', required: false, default: 'json'

Parameters:

  • name (Symbol)

    the parameter name

  • type (Symbol) (defaults to: :string)

    the parameter type (:string, :integer, :boolean, etc.)

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

    optional description of the parameter

  • required (Boolean) (defaults to: true)

    whether the parameter is required (default: true)

  • default (Object, nil) (defaults to: nil)

    default value for optional parameters



92
93
94
95
96
97
98
99
100
# File 'lib/tool_forge/tool_definition.rb', line 92

def param(name, type: :string, description: nil, required: true, default: nil)
  @params << {
    name: name,
    type: type,
    description: description,
    required: required,
    default: default
  }
end

#to_mcp_toolClass

Converts this tool definition to an MCP::Tool class. The resulting class can be used with the Model Context Protocol framework.

Both instance and class helper methods are available in the execution context. Class helper methods are accessed via self.class.method_name.

Examples:

tool_class = tool_definition.to_mcp_tool
result = tool_class.call(server_context: nil, filename: 'data.txt')

Returns:

  • (Class)

    a class that inherits from MCP::Tool

Raises:

  • (LoadError)

    if MCP SDK is not loaded



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/tool_forge/tool_definition.rb', line 211

def to_mcp_tool
  raise LoadError, 'MCP SDK is not loaded. Please require "mcp" first.' unless defined?(MCP::Tool)
  raise LoadError, 'MCP SDK is not loaded. Please require "mcp" first.' if MCP::Tool.nil?

  definition = self

  Class.new(MCP::Tool) do
    description definition.description

    # Build properties hash for input schema
    properties = {}
    required_params = []

    definition.params.each do |param_def|
      prop = {
        type: param_def[:type].to_s
      }
      prop[:description] = param_def[:description] if param_def[:description]

      properties[param_def[:name].to_s] = prop
      required_params << param_def[:name].to_s if param_def[:required]
    end

    input_schema(
      properties: properties,
      required: required_params
    )

    # Create a helper object that contains all the helper methods
    helper_class = Class.new do
      definition.helper_methods[:instance].each do |method_name, method_block|
        define_method(method_name, &method_block)
      end

      # Class methods are defined as singleton methods on the class itself
      definition.helper_methods[:class].each do |method_name, method_block|
        define_singleton_method(method_name, &method_block)
      end
    end

    define_singleton_method(:call) do |server_context:, **args|
      # Create an instance of the helper class to provide context for helper methods
      helper_instance = helper_class.new

      # Execute the block in the context of the helper instance so helper methods are available
      # For class methods, they'll be available on the helper_class itself
      result = helper_instance.instance_exec(**args, &definition.execute_block)

      # Smart formatting for different return types
      result_text = case result
                    when String
                      result
                    when Hash, Array
                      JSON.pretty_generate(result)
                    else
                      result.to_s
                    end

      MCP::Tool::Response.new([{ type: 'text', text: result_text }])
    end
  end
end

#to_ruby_llm_toolClass

Converts this tool definition to a RubyLLM::Tool class. The resulting class can be instantiated and used with the RubyLLM framework.

Instance helper methods become instance methods on the generated class. Class helper methods become singleton methods on the generated class.

Examples:

tool_class = tool_definition.to_ruby_llm_tool
instance = tool_class.new
result = instance.execute(filename: 'data.txt')

Returns:

  • (Class)

    a class that inherits from RubyLLM::Tool

Raises:

  • (LoadError)

    if RubyLLM is not loaded



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/tool_forge/tool_definition.rb', line 169

def to_ruby_llm_tool
  raise LoadError, 'RubyLLM is not loaded. Please require "ruby_llm" first.' unless defined?(RubyLLM::Tool)
  raise LoadError, 'RubyLLM is not loaded. Please require "ruby_llm" first.' if RubyLLM::Tool.nil?

  definition = self

  Class.new(RubyLLM::Tool) do
    description definition.description

    definition.params.each do |param_def|
      param param_def[:name], type: param_def[:type], desc: param_def[:description]
    end

    # Add instance helper methods
    definition.helper_methods[:instance].each do |method_name, method_block|
      define_method(method_name, &method_block)
    end

    # Add class helper methods
    definition.helper_methods[:class].each do |method_name, method_block|
      define_singleton_method(method_name, &method_block)
    end

    define_method(:execute) do |**args|
      # Execute the block in the context of this instance so helper methods are available
      instance_exec(**args, &definition.execute_block)
    end
  end
end