Class: Aspera::Cli::McpTool
- Inherits:
-
MCP::Tool
- Object
- MCP::Tool
- Aspera::Cli::McpTool
- Defined in:
- lib/aspera/cli/mcp_tool.rb
Overview
MCP Tool: executes an ascli command in-process.
Constant Summary collapse
- DEFAULT_MAX_TEXT_BYTES =
Default maximum byte size of the JSON text content returned for list results. Items are appended whole until the limit is reached; the full list is always available in structuredContent.
100_000- DEFAULT_EXTRA_ARGS =
Default extra arguments automatically prepended to every ascli call. Keeps the AI from having to remember mandatory flags on every invocation.
['--interactive=no', '--transfer.asynchronous=true'].freeze
Class Attribute Summary collapse
-
.extra_args ⇒ Object
Returns the value of attribute extra_args.
-
.max_text_bytes ⇒ Object
Returns the value of attribute max_text_bytes.
Class Method Summary collapse
- .call(args:, server_context: nil) ⇒ Object
-
.truncate_items_by_bytes(items, max_bytes) ⇒ Object
Returns the largest prefix of
itemswhose JSON serialization fits withinmax_bytes.
Class Attribute Details
.extra_args ⇒ Object
Returns the value of attribute extra_args.
144 145 146 |
# File 'lib/aspera/cli/mcp_tool.rb', line 144 def extra_args @extra_args end |
.max_text_bytes ⇒ Object
Returns the value of attribute max_text_bytes.
144 145 146 |
# File 'lib/aspera/cli/mcp_tool.rb', line 144 def max_text_bytes @max_text_bytes end |
Class Method Details
.call(args:, server_context: nil) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/aspera/cli/mcp_tool.rb', line 146 def call(args:, server_context: nil) effective_args = Array(extra_args || DEFAULT_EXTRA_ARGS) + args Log.dump(:mcp_execute, effective_args) runner = Runner.new(effective_args) result = runner.run_with_result case result when Result::Nothing, Result::Empty, NilClass MCP::Tool::Response.new([{type: 'text', text: ''}]) when Result::SingleObject, Result::ObjectList, Result::ValueList # Apply --select filter in place (affects both text and structuredContent). runner.context.formatter.filter_columns_on_select(result.data) if result.data.is_a?(Array) # MCP spec requires structuredContent to be a JSON object (not an array). structured = result.data.is_a?(Array) ? {items: result.data} : result.data content = if result.data.is_a?(Array) text_limit = max_text_bytes || DEFAULT_MAX_TEXT_BYTES truncated_items = truncate_items_by_bytes(result.data, text_limit) total = result.data.size if truncated_items.size < total [ {type: 'text', text: JSON.generate(truncated_items)}, {type: 'text', text: "WARNING: result truncated to #{truncated_items.size} of #{total} items. Full dataset available in structuredContent."} ] else [{type: 'text', text: JSON.generate(result.data)}] end else [{type: 'text', text: JSON.generate(result.data)}] end MCP::Tool::Response.new(content, structured_content: structured) else MCP::Tool::Response.new([{type: 'text', text: result.data.to_s}]) end rescue SystemExit => e MCP::Tool::Response.new([{type: 'text', text: "exited with status #{e.status}"}], error: !e.status.zero?) rescue => e MCP::Tool::Response.new([{type: 'text', text: "#{e.class}: #{e.}"}], error: true) end |
.truncate_items_by_bytes(items, max_bytes) ⇒ Object
Returns the largest prefix of items whose JSON serialization fits within max_bytes.
Items are appended whole — no item is ever split mid-JSON.
186 187 188 189 190 191 192 193 194 |
# File 'lib/aspera/cli/mcp_tool.rb', line 186 def truncate_items_by_bytes(items, max_bytes) buf = +'' items.each_with_index do |item, i| fragment = (i.zero? ? '[' : ',') + JSON.generate(item) break if buf.bytesize + fragment.bytesize + 1 > max_bytes # +1 for closing ']' buf << fragment end buf.empty? ? [] : JSON.parse("#{buf}]") end |