Class: RailsAiContext::CLI::ToolRunner
- Inherits:
-
Object
- Object
- RailsAiContext::CLI::ToolRunner
- Defined in:
- lib/rails_ai_context/cli/tool_runner.rb
Overview
Runs MCP tools from the command line without requiring an MCP client. Reads tool schemas at runtime - no hardcoded parameter lists.
Usage:
runner = ToolRunner.new("schema", ["--table", "users", "--detail", "full"])
puts runner.run
runner = ToolRunner.new("schema", { table: "users", detail: "full" })
puts runner.run
Defined Under Namespace
Classes: InvalidArgumentError, ToolNotFoundError
Instance Attribute Summary collapse
-
#error ⇒ Object
readonly
Returns the value of attribute error.
-
#json_mode ⇒ Object
readonly
Returns the value of attribute json_mode.
-
#raw_args ⇒ Object
readonly
Returns the value of attribute raw_args.
-
#tool_class ⇒ Object
readonly
Returns the value of attribute tool_class.
Class Method Summary collapse
-
.available_tools ⇒ Object
Filtered tool list respecting skip_tools config.
-
.short_name(tool_name) ⇒ Object
Derive short name: rails_get_schema → schema, rails_analyze_feature → analyze_feature.
-
.tool_help(tool_class) ⇒ Object
Generate help for a specific tool from its input_schema.
-
.tool_list ⇒ Object
List all available tools with short names and descriptions.
-
.truncate_at_word(text, limit) ⇒ Object
Truncate at the last whole word within
limitchars and append "..." so descriptions never cut off mid-word.
Instance Method Summary collapse
-
#initialize(tool_name, raw_args, json_mode: false) ⇒ ToolRunner
constructor
A new instance of ToolRunner.
- #run ⇒ Object
Constructor Details
#initialize(tool_name, raw_args, json_mode: false) ⇒ ToolRunner
Returns a new instance of ToolRunner.
20 21 22 23 24 25 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 20 def initialize(tool_name, raw_args, json_mode: false) @tool_class = resolve_tool(tool_name) @raw_args = raw_args @json_mode = json_mode @error = false end |
Instance Attribute Details
#error ⇒ Object (readonly)
Returns the value of attribute error.
18 19 20 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 18 def error @error end |
#json_mode ⇒ Object (readonly)
Returns the value of attribute json_mode.
18 19 20 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 18 def json_mode @json_mode end |
#raw_args ⇒ Object (readonly)
Returns the value of attribute raw_args.
18 19 20 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 18 def raw_args @raw_args end |
#tool_class ⇒ Object (readonly)
Returns the value of attribute tool_class.
18 19 20 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 18 def tool_class @tool_class end |
Class Method Details
.available_tools ⇒ Object
Filtered tool list respecting skip_tools config.
70 71 72 73 74 75 76 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 70 def self.available_tools skip = RailsAiContext.configuration.skip_tools tools = Server.builtin_tools tools += RailsAiContext.configuration.custom_tools return tools if skip.empty? tools.reject { |t| skip.include?(t.tool_name) } end |
.short_name(tool_name) ⇒ Object
Derive short name: rails_get_schema → schema, rails_analyze_feature → analyze_feature
115 116 117 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 115 def self.short_name(tool_name) tool_name.sub(/\Arails_get_/, "").sub(/\Arails_/, "") end |
.tool_help(tool_class) ⇒ Object
Generate help for a specific tool from its input_schema.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 79 def self.tool_help(tool_class) schema = tool_class.input_schema_value&.to_h || {} properties = schema[:properties] || {} required = schema[:required] || [] lines = [ "#{tool_class.tool_name} - #{tool_class.description_value}", "", "Usage:" ] # Standalone installs have no rake tasks; only show the rake form when # the gem lives in the app's Gemfile. unless RailsAiContext::InstallMode.standalone? lines << " rails 'ai:tool[#{short_name(tool_class.tool_name)}]' #{properties.keys.map { |k| "#{k}=VALUE" }.join(' ')}" end lines << " rails-ai-context tool #{short_name(tool_class.tool_name)} #{properties.keys.map { |k| "--#{k.to_s.tr('_', '-')} VALUE" }.join(' ')}" lines << "" if properties.any? lines << "Options:" properties.each do |name, prop| flag = "--#{name.to_s.tr('_', '-')}" type_hint = prop[:type] || "string" type_hint = "#{type_hint} (#{prop[:enum].join('/')})" if prop[:enum] req = required.include?(name.to_s) ? " [required]" : "" desc = prop[:description] || "" lines << " #{flag.ljust(24)} #{desc} (#{type_hint})#{req}" end else lines << " No parameters." end lines.join("\n") end |
.tool_list ⇒ Object
List all available tools with short names and descriptions.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 36 def self.tool_list lines = [ "Available tools:", "" ] available_tools.each do |tool| short = short_name(tool.tool_name) desc = truncate_at_word(tool.description_value.to_s, 79) lines << " #{short.ljust(24)} #{desc}" end lines << "" # Standalone installs have no rake tasks, so advertising the rake form # would point users at a command that does not exist. if RailsAiContext::InstallMode.standalone? lines << "Usage: rails-ai-context tool NAME --param value" lines << "JSON envelope: rails-ai-context tool NAME --json" else lines << "Usage: rails 'ai:tool[NAME]' param=value" lines << " rails-ai-context tool NAME --param value" lines << "JSON envelope: rails-ai-context tool NAME --json, or JSON=1 rails 'ai:tool[NAME]'" end lines.join("\n") end |
.truncate_at_word(text, limit) ⇒ Object
Truncate at the last whole word within limit chars and append "..."
so descriptions never cut off mid-word. Returns text unchanged when
it already fits.
60 61 62 63 64 65 66 67 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 60 def self.truncate_at_word(text, limit) return text if text.length <= limit cut = text[0...limit] boundary = cut.rindex(" ") cut = cut[0...boundary] if boundary "#{cut}..." end |
Instance Method Details
#run ⇒ Object
27 28 29 30 31 32 33 |
# File 'lib/rails_ai_context/cli/tool_runner.rb', line 27 def run kwargs = build_kwargs schema = tool_schema validate_kwargs!(kwargs, schema) response = tool_class.call(**kwargs) extract_output(response) end |