Class: Plasma::Tool

Inherits:
ModelContextProtocol::Server::Tool
  • Object
show all
Defined in:
lib/plasma/tool.rb

Overview

Base tool class for PLASMA applications

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params) ⇒ Tool

rubocop:disable Lint/MissingSuper DO NOT call super as we are deliberately decoupling the tool from the underlying library



72
73
74
75
# File 'lib/plasma/tool.rb', line 72

def initialize(params)
  @raw_params = params
  @params = coerce_and_validate!(params)
end

Instance Attribute Details

#paramsObject (readonly)

rubocop:enable Lint/MissingSuper



78
79
80
# File 'lib/plasma/tool.rb', line 78

def params
  @params
end

Class Method Details

.defined_paramsObject



19
20
21
# File 'lib/plasma/tool.rb', line 19

def defined_params
  @params || {}
end

.extract_descriptionObject



41
42
43
44
45
# File 'lib/plasma/tool.rb', line 41

def extract_description
  return @description if @description

  @description = read_comments_from_file(file_path).presence || "No description available"
end

.file_pathObject



47
48
49
50
51
52
53
54
55
# File 'lib/plasma/tool.rb', line 47

def file_path
  loader = Zeitwerk::Registry.loaders.first
  module_name = to_s.split("::")[0..-2].join("::").constantize
  class_name = to_s.split("::").last.to_sym
  path = loader.instance_variable_get(:@inceptions).instance_variable_get(:@map)[module_name][class_name]
  return nil unless path

  File.expand_path(path)
end

.inherited(subclass) ⇒ Object



23
24
25
26
27
# File 'lib/plasma/tool.rb', line 23

def inherited(subclass)
  super
  const_name = subclass.to_s.split("::").last
  Plasma.const_set(const_name, subclass) unless Plasma.const_defined?(const_name)
end

.metadataObject



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/plasma/tool.rb', line 29

def 
  {
    name: to_s.split("::").last.gsub("Tool", "").underscore,
    description: extract_description,
    inputSchema: {
      type: "object",
      properties: defined_params.transform_values { |v| v.slice(:type, :description).compact },
      required: defined_params.select { |_, v| v[:required] }.keys.map(&:to_s)
    }
  }
end

.param(name, type:, description: nil, required: false) ⇒ Object



10
11
12
13
14
15
16
17
# File 'lib/plasma/tool.rb', line 10

def param(name, type:, description: nil, required: false)
  @params ||= {}
  @params[name.to_sym] = {
    type: type.to_s.downcase,
    description:,
    required:
  }
end

.read_comments_from_file(file_path) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
# File 'lib/plasma/tool.rb', line 57

def read_comments_from_file(file_path)
  return "No description available" unless file_path

  File.readlines(file_path)
      .drop_while { |line| line.include?("frozen_string_literal") || line.strip.empty? }
      .drop_while { |line| line.strip.start_with?("module") }
      .take_while { |line| line.strip.start_with?("#") }
      .join
      .strip
      .gsub(/^#\s*/, "")
end