actionmcp 0.80.1 → 0.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/README.md +62 -3
 - data/lib/action_mcp/capability.rb +0 -1
 - data/lib/action_mcp/client/base.rb +0 -1
 - data/lib/action_mcp/client/transport.rb +0 -1
 - data/lib/action_mcp/configuration.rb +4 -4
 - data/lib/action_mcp/engine.rb +5 -0
 - data/lib/action_mcp/gateway.rb +16 -3
 - data/lib/action_mcp/json_rpc_handler_base.rb +3 -0
 - data/lib/action_mcp/logging/level.rb +84 -0
 - data/lib/action_mcp/logging/logger.rb +208 -0
 - data/lib/action_mcp/logging/mixin.rb +149 -0
 - data/lib/action_mcp/logging/null_logger.rb +94 -0
 - data/lib/action_mcp/logging/state.rb +83 -0
 - data/lib/action_mcp/logging.rb +81 -5
 - data/lib/action_mcp/output_schema_builder.rb +184 -0
 - data/lib/action_mcp/resource_template.rb +45 -34
 - data/lib/action_mcp/schema_helpers.rb +36 -0
 - data/lib/action_mcp/server/handlers/logging_handler.rb +43 -0
 - data/lib/action_mcp/server/json_rpc_handler.rb +3 -0
 - data/lib/action_mcp/server/tools.rb +4 -5
 - data/lib/action_mcp/server/transport_handler.rb +0 -1
 - data/lib/action_mcp/tool.rb +110 -25
 - data/lib/action_mcp/tool_response.rb +26 -3
 - data/lib/action_mcp/tools_registry.rb +7 -2
 - data/lib/action_mcp/version.rb +1 -1
 - data/lib/action_mcp.rb +0 -1
 - data/lib/generators/action_mcp/tool/templates/tool.rb.erb +12 -1
 - metadata +13 -19
 
    
        data/lib/action_mcp/tool.rb
    CHANGED
    
    | 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require "action_mcp/types/float_array_type"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "action_mcp/schema_helpers"
         
     | 
| 
       4 
5 
     | 
    
         | 
| 
       5 
6 
     | 
    
         
             
            module ActionMCP
         
     | 
| 
       6 
7 
     | 
    
         
             
              # Base class for defining tools.
         
     | 
| 
         @@ -10,6 +11,7 @@ module ActionMCP 
     | 
|
| 
       10 
11 
     | 
    
         
             
              class Tool < Capability
         
     | 
| 
       11 
12 
     | 
    
         
             
                include ActionMCP::Callbacks
         
     | 
| 
       12 
13 
     | 
    
         
             
                include ActionMCP::CurrentHelpers
         
     | 
| 
      
 14 
     | 
    
         
            +
                extend ActionMCP::SchemaHelpers
         
     | 
| 
       13 
15 
     | 
    
         | 
| 
       14 
16 
     | 
    
         
             
                # --------------------------------------------------------------------------
         
     | 
| 
       15 
17 
     | 
    
         
             
                # Class Attributes for Tool Metadata and Schema
         
     | 
| 
         @@ -24,6 +26,9 @@ module ActionMCP 
     | 
|
| 
       24 
26 
     | 
    
         
             
                class_attribute :_output_schema, instance_accessor: false, default: nil
         
     | 
| 
       25 
27 
     | 
    
         
             
                class_attribute :_meta, instance_accessor: false, default: {}
         
     | 
| 
       26 
28 
     | 
    
         
             
                class_attribute :_requires_consent, instance_accessor: false, default: false
         
     | 
| 
      
 29 
     | 
    
         
            +
                class_attribute :_output_schema_builder, instance_accessor: false, default: nil
         
     | 
| 
      
 30 
     | 
    
         
            +
                class_attribute :_additional_properties, instance_accessor: false, default: nil
         
     | 
| 
      
 31 
     | 
    
         
            +
                class_attribute :_cached_schema_property_keys, instance_accessor: false, default: nil
         
     | 
| 
       27 
32 
     | 
    
         | 
| 
       28 
33 
     | 
    
         
             
                # --------------------------------------------------------------------------
         
     | 
| 
       29 
34 
     | 
    
         
             
                # Tool Name and Description DSL
         
     | 
| 
         @@ -126,10 +131,27 @@ module ActionMCP 
     | 
|
| 
       126 
131 
     | 
    
         
             
                    _annotations["openWorldHint"] == true
         
     | 
| 
       127 
132 
     | 
    
         
             
                  end
         
     | 
| 
       128 
133 
     | 
    
         | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
                   
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  # Schema DSL for output structure
         
     | 
| 
      
 136 
     | 
    
         
            +
                  # @param block [Proc] Block containing output schema definition
         
     | 
| 
      
 137 
     | 
    
         
            +
                  # @return [Hash] The generated JSON Schema
         
     | 
| 
      
 138 
     | 
    
         
            +
                  def output_schema(&block)
         
     | 
| 
      
 139 
     | 
    
         
            +
                    return _output_schema unless block_given?
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                    builder = OutputSchemaBuilder.new
         
     | 
| 
      
 142 
     | 
    
         
            +
                    builder.instance_eval(&block)
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                    # Store both the builder and the generated schema
         
     | 
| 
      
 145 
     | 
    
         
            +
                    self._output_schema_builder = builder
         
     | 
| 
      
 146 
     | 
    
         
            +
                    self._output_schema = builder.to_json_schema
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                    _output_schema
         
     | 
| 
      
 149 
     | 
    
         
            +
                  end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                  # Legacy output_schema method for backward compatibility
         
     | 
| 
      
 152 
     | 
    
         
            +
                  def output_schema_legacy(schema = nil)
         
     | 
| 
       131 
153 
     | 
    
         
             
                    if schema
         
     | 
| 
       132 
     | 
    
         
            -
                      raise NotImplementedError, " 
     | 
| 
      
 154 
     | 
    
         
            +
                      raise NotImplementedError, "Legacy output schema not yet implemented. Use output_schema DSL instead!"
         
     | 
| 
       133 
155 
     | 
    
         
             
                    end
         
     | 
| 
       134 
156 
     | 
    
         | 
| 
       135 
157 
     | 
    
         
             
                    _output_schema
         
     | 
| 
         @@ -155,6 +177,45 @@ module ActionMCP 
     | 
|
| 
       155 
177 
     | 
    
         
             
                  def requires_consent?
         
     | 
| 
       156 
178 
     | 
    
         
             
                    _requires_consent
         
     | 
| 
       157 
179 
     | 
    
         
             
                  end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                  # Sets or retrieves the additionalProperties setting for the input schema
         
     | 
| 
      
 182 
     | 
    
         
            +
                  # @param enabled [Boolean, Hash] true to allow any additional properties,
         
     | 
| 
      
 183 
     | 
    
         
            +
                  #   false to disallow them, or a Hash for typed additional properties
         
     | 
| 
      
 184 
     | 
    
         
            +
                  def additional_properties(enabled = nil)
         
     | 
| 
      
 185 
     | 
    
         
            +
                    if enabled.nil?
         
     | 
| 
      
 186 
     | 
    
         
            +
                      _additional_properties
         
     | 
| 
      
 187 
     | 
    
         
            +
                    else
         
     | 
| 
      
 188 
     | 
    
         
            +
                      self._additional_properties = enabled
         
     | 
| 
      
 189 
     | 
    
         
            +
                    end
         
     | 
| 
      
 190 
     | 
    
         
            +
                  end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                  # Returns whether this tool accepts additional properties
         
     | 
| 
      
 193 
     | 
    
         
            +
                  def accepts_additional_properties?
         
     | 
| 
      
 194 
     | 
    
         
            +
                    !_additional_properties.nil? && _additional_properties != false
         
     | 
| 
      
 195 
     | 
    
         
            +
                  end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                  # Returns cached string keys for schema properties to avoid repeated conversions
         
     | 
| 
      
 198 
     | 
    
         
            +
                  def schema_property_keys
         
     | 
| 
      
 199 
     | 
    
         
            +
                    return _cached_schema_property_keys if _cached_schema_property_keys
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                    self._cached_schema_property_keys = _schema_properties.keys.map(&:to_s)
         
     | 
| 
      
 202 
     | 
    
         
            +
                    _cached_schema_property_keys
         
     | 
| 
      
 203 
     | 
    
         
            +
                  end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                  # Clear cached keys when properties change - use metaprogramming to avoid duplication
         
     | 
| 
      
 206 
     | 
    
         
            +
                  [ :property, :collection ].each do |method_name|
         
     | 
| 
      
 207 
     | 
    
         
            +
                    define_method(method_name) do |prop_name, **opts|
         
     | 
| 
      
 208 
     | 
    
         
            +
                      invalidate_schema_cache
         
     | 
| 
      
 209 
     | 
    
         
            +
                      super(prop_name, **opts)
         
     | 
| 
      
 210 
     | 
    
         
            +
                    end
         
     | 
| 
      
 211 
     | 
    
         
            +
                  end
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                  private
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                  # Invalidate cached schema property keys
         
     | 
| 
      
 216 
     | 
    
         
            +
                  def invalidate_schema_cache
         
     | 
| 
      
 217 
     | 
    
         
            +
                    self._cached_schema_property_keys = nil
         
     | 
| 
      
 218 
     | 
    
         
            +
                  end
         
     | 
| 
       158 
219 
     | 
    
         
             
                end
         
     | 
| 
       159 
220 
     | 
    
         | 
| 
       160 
221 
     | 
    
         
             
                # --------------------------------------------------------------------------
         
     | 
| 
         @@ -179,9 +240,9 @@ module ActionMCP 
     | 
|
| 
       179 
240 
     | 
    
         
             
                  prop_definition.merge!(opts) if opts.any?
         
     | 
| 
       180 
241 
     | 
    
         | 
| 
       181 
242 
     | 
    
         
             
                  self._schema_properties = _schema_properties.merge(prop_name.to_s => prop_definition)
         
     | 
| 
       182 
     | 
    
         
            -
                   
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
                   
     | 
| 
      
 243 
     | 
    
         
            +
                  new_required = _required_properties.dup
         
     | 
| 
      
 244 
     | 
    
         
            +
                  new_required << prop_name.to_s if required
         
     | 
| 
      
 245 
     | 
    
         
            +
                  self._required_properties = new_required
         
     | 
| 
       185 
246 
     | 
    
         | 
| 
       186 
247 
     | 
    
         
             
                  # Map the JSON Schema type to an ActiveModel attribute type.
         
     | 
| 
       187 
248 
     | 
    
         
             
                  attribute prop_name, map_json_type_to_active_model_type(type), default: default
         
     | 
| 
         @@ -210,9 +271,9 @@ module ActionMCP 
     | 
|
| 
       210 
271 
     | 
    
         
             
                  collection_definition[:description] = description if description && !description.empty?
         
     | 
| 
       211 
272 
     | 
    
         | 
| 
       212 
273 
     | 
    
         
             
                  self._schema_properties = _schema_properties.merge(prop_name.to_s => collection_definition)
         
     | 
| 
       213 
     | 
    
         
            -
                   
     | 
| 
       214 
     | 
    
         
            -
             
     | 
| 
       215 
     | 
    
         
            -
                   
     | 
| 
      
 274 
     | 
    
         
            +
                  new_required = _required_properties.dup
         
     | 
| 
      
 275 
     | 
    
         
            +
                  new_required << prop_name.to_s if required
         
     | 
| 
      
 276 
     | 
    
         
            +
                  self._required_properties = new_required
         
     | 
| 
       216 
277 
     | 
    
         | 
| 
       217 
278 
     | 
    
         
             
                  # Map the type - for number arrays, use our custom type instance
         
     | 
| 
       218 
279 
     | 
    
         
             
                  mapped_type = if type == "number"
         
     | 
| 
         @@ -245,6 +306,9 @@ module ActionMCP 
     | 
|
| 
       245 
306 
     | 
    
         
             
                  }
         
     | 
| 
       246 
307 
     | 
    
         
             
                  schema[:required] = _required_properties if _required_properties.any?
         
     | 
| 
       247 
308 
     | 
    
         | 
| 
      
 309 
     | 
    
         
            +
                  # Add additionalProperties if configured
         
     | 
| 
      
 310 
     | 
    
         
            +
                  add_additional_properties_to_schema(schema, _additional_properties)
         
     | 
| 
      
 311 
     | 
    
         
            +
             
     | 
| 
       248 
312 
     | 
    
         
             
                  result = {
         
     | 
| 
       249 
313 
     | 
    
         
             
                    name: tool_name,
         
     | 
| 
       250 
314 
     | 
    
         
             
                    description: description.presence,
         
     | 
| 
         @@ -270,11 +334,29 @@ module ActionMCP 
     | 
|
| 
       270 
334 
     | 
    
         | 
| 
       271 
335 
     | 
    
         
             
                # Override initialize to validate parameters before ActiveModel conversion
         
     | 
| 
       272 
336 
     | 
    
         
             
                def initialize(attributes = {})
         
     | 
| 
      
 337 
     | 
    
         
            +
                  # Separate additional properties from defined attributes if enabled
         
     | 
| 
      
 338 
     | 
    
         
            +
                  if self.class.accepts_additional_properties?
         
     | 
| 
      
 339 
     | 
    
         
            +
                    defined_keys = self.class.schema_property_keys
         
     | 
| 
      
 340 
     | 
    
         
            +
                    # Use partition for single-pass separation - more efficient than except/slice
         
     | 
| 
      
 341 
     | 
    
         
            +
                    defined_attrs, additional_attrs = attributes.partition { |k, _|
         
     | 
| 
      
 342 
     | 
    
         
            +
                      defined_keys.include?(k.to_s)
         
     | 
| 
      
 343 
     | 
    
         
            +
                    }.map(&:to_h)
         
     | 
| 
      
 344 
     | 
    
         
            +
                    @_additional_params = additional_attrs
         
     | 
| 
      
 345 
     | 
    
         
            +
                    attributes = defined_attrs
         
     | 
| 
      
 346 
     | 
    
         
            +
                  else
         
     | 
| 
      
 347 
     | 
    
         
            +
                    @_additional_params = {}
         
     | 
| 
      
 348 
     | 
    
         
            +
                  end
         
     | 
| 
      
 349 
     | 
    
         
            +
             
     | 
| 
       273 
350 
     | 
    
         
             
                  # Validate parameters before ActiveModel processes them
         
     | 
| 
       274 
351 
     | 
    
         
             
                  validate_parameter_types(attributes)
         
     | 
| 
       275 
352 
     | 
    
         
             
                  super
         
     | 
| 
       276 
353 
     | 
    
         
             
                end
         
     | 
| 
       277 
354 
     | 
    
         | 
| 
      
 355 
     | 
    
         
            +
                # Returns additional parameters that were passed but not defined in the schema
         
     | 
| 
      
 356 
     | 
    
         
            +
                def additional_params
         
     | 
| 
      
 357 
     | 
    
         
            +
                  @_additional_params || {}
         
     | 
| 
      
 358 
     | 
    
         
            +
                end
         
     | 
| 
      
 359 
     | 
    
         
            +
             
     | 
| 
       278 
360 
     | 
    
         
             
                # Public entry point for executing the tool
         
     | 
| 
       279 
361 
     | 
    
         
             
                # Returns an array of Content objects collected from render calls
         
     | 
| 
       280 
362 
     | 
    
         
             
                def call
         
     | 
| 
         @@ -290,9 +372,9 @@ module ActionMCP 
     | 
|
| 
       290 
372 
     | 
    
         
             
                    rescue StandardError => e
         
     | 
| 
       291 
373 
     | 
    
         
             
                      # Show generic error message for HTTP requests, detailed for direct calls
         
     | 
| 
       292 
374 
     | 
    
         
             
                      error_message = if execution_context[:request].present?
         
     | 
| 
       293 
     | 
    
         
            -
             
     | 
| 
      
 375 
     | 
    
         
            +
                                         "An unexpected error occurred."
         
     | 
| 
       294 
376 
     | 
    
         
             
                      else
         
     | 
| 
       295 
     | 
    
         
            -
             
     | 
| 
      
 377 
     | 
    
         
            +
                        e.message
         
     | 
| 
       296 
378 
     | 
    
         
             
                      end
         
     | 
| 
       297 
379 
     | 
    
         
             
                      @response.mark_as_error!(:internal_error, message: error_message)
         
     | 
| 
       298 
380 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -327,11 +409,18 @@ module ActionMCP 
     | 
|
| 
       327 
409 
     | 
    
         
             
                  end.join(', ')}, #{response_info}#{errors_info}>"
         
     | 
| 
       328 
410 
     | 
    
         
             
                end
         
     | 
| 
       329 
411 
     | 
    
         | 
| 
       330 
     | 
    
         
            -
                # Override render to collect Content objects
         
     | 
| 
       331 
     | 
    
         
            -
                def render(**args)
         
     | 
| 
       332 
     | 
    
         
            -
                   
     | 
| 
       333 
     | 
    
         
            -
             
     | 
| 
       334 
     | 
    
         
            -
             
     | 
| 
      
 412 
     | 
    
         
            +
                # Override render to collect Content objects and support structured content
         
     | 
| 
      
 413 
     | 
    
         
            +
                def render(structured: nil, **args)
         
     | 
| 
      
 414 
     | 
    
         
            +
                  if structured
         
     | 
| 
      
 415 
     | 
    
         
            +
                    # Render structured content
         
     | 
| 
      
 416 
     | 
    
         
            +
                    set_structured_content(structured)
         
     | 
| 
      
 417 
     | 
    
         
            +
                    structured
         
     | 
| 
      
 418 
     | 
    
         
            +
                  else
         
     | 
| 
      
 419 
     | 
    
         
            +
                    # Normal content rendering
         
     | 
| 
      
 420 
     | 
    
         
            +
                    content = super(**args) # Call Renderable's render method
         
     | 
| 
      
 421 
     | 
    
         
            +
                    @response.add(content)  # Add to the response
         
     | 
| 
      
 422 
     | 
    
         
            +
                    content # Return the content for potential use in perform
         
     | 
| 
      
 423 
     | 
    
         
            +
                  end
         
     | 
| 
       335 
424 
     | 
    
         
             
                end
         
     | 
| 
       336 
425 
     | 
    
         | 
| 
       337 
426 
     | 
    
         
             
                # Override render_resource_link to collect ResourceLink objects
         
     | 
| 
         @@ -352,25 +441,21 @@ module ActionMCP 
     | 
|
| 
       352 
441 
     | 
    
         
             
                private
         
     | 
| 
       353 
442 
     | 
    
         | 
| 
       354 
443 
     | 
    
         
             
                # Helper method for tools to manually report errors
         
     | 
| 
      
 444 
     | 
    
         
            +
                # Uses the MCP-compliant tool execution error format
         
     | 
| 
       355 
445 
     | 
    
         
             
                def report_error(message)
         
     | 
| 
       356 
     | 
    
         
            -
                  @response. 
     | 
| 
       357 
     | 
    
         
            -
                  render text: message
         
     | 
| 
      
 446 
     | 
    
         
            +
                  @response.report_tool_error(message)
         
     | 
| 
       358 
447 
     | 
    
         
             
                end
         
     | 
| 
       359 
448 
     | 
    
         | 
| 
       360 
449 
     | 
    
         
             
                # Helper method to set structured content
         
     | 
| 
       361 
450 
     | 
    
         
             
                def set_structured_content(content)
         
     | 
| 
       362 
451 
     | 
    
         
             
                  return unless @response
         
     | 
| 
       363 
452 
     | 
    
         | 
| 
       364 
     | 
    
         
            -
                  # Validate against output schema if defined
         
     | 
| 
       365 
     | 
    
         
            -
                  # TODO: Add JSON Schema validation here
         
     | 
| 
       366 
     | 
    
         
            -
                  # For now, just ensure it's a hash/object
         
     | 
| 
       367 
     | 
    
         
            -
                  if self.class._output_schema && !content.is_a?(Hash)
         
     | 
| 
       368 
     | 
    
         
            -
                    raise ArgumentError, "Structured content must be a hash/object when output_schema is defined"
         
     | 
| 
       369 
     | 
    
         
            -
                  end
         
     | 
| 
       370 
     | 
    
         
            -
             
     | 
| 
       371 
453 
     | 
    
         
             
                  @response.set_structured_content(content)
         
     | 
| 
       372 
454 
     | 
    
         
             
                end
         
     | 
| 
       373 
455 
     | 
    
         | 
| 
      
 456 
     | 
    
         
            +
                private
         
     | 
| 
      
 457 
     | 
    
         
            +
             
     | 
| 
      
 458 
     | 
    
         
            +
             
     | 
| 
       374 
459 
     | 
    
         
             
                # Maps a JSON Schema type to an ActiveModel attribute type.
         
     | 
| 
       375 
460 
     | 
    
         
             
                #
         
     | 
| 
       376 
461 
     | 
    
         
             
                # @param type [String] The JSON Schema type.
         
     | 
| 
         @@ -3,7 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module ActionMCP
         
     | 
| 
       4 
4 
     | 
    
         
             
              # Manages the collection of content objects for tool results
         
     | 
| 
       5 
5 
     | 
    
         
             
              class ToolResponse < BaseResponse
         
     | 
| 
       6 
     | 
    
         
            -
                attr_reader :contents, :structured_content
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_reader :contents, :structured_content, :tool_execution_error
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                delegate :empty?, :size, :each, :find, :map, to: :contents
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
         @@ -11,6 +11,7 @@ module ActionMCP 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  super
         
     | 
| 
       12 
12 
     | 
    
         
             
                  @contents = []
         
     | 
| 
       13 
13 
     | 
    
         
             
                  @structured_content = nil
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @tool_execution_error = false  # Track if this is a tool execution error
         
     | 
| 
       14 
15 
     | 
    
         
             
                end
         
     | 
| 
       15 
16 
     | 
    
         | 
| 
       16 
17 
     | 
    
         
             
                # Add content to the response
         
     | 
| 
         @@ -24,6 +25,26 @@ module ActionMCP 
     | 
|
| 
       24 
25 
     | 
    
         
             
                  @structured_content = content
         
     | 
| 
       25 
26 
     | 
    
         
             
                end
         
     | 
| 
       26 
27 
     | 
    
         | 
| 
      
 28 
     | 
    
         
            +
                # Report a tool execution error (as opposed to protocol error)
         
     | 
| 
      
 29 
     | 
    
         
            +
                # This follows MCP spec for tool execution errors
         
     | 
| 
      
 30 
     | 
    
         
            +
                def report_tool_error(message)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @tool_execution_error = true
         
     | 
| 
      
 32 
     | 
    
         
            +
                  add(Content::Text.new(message))
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def to_h(_options = nil)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  if @tool_execution_error
         
     | 
| 
      
 37 
     | 
    
         
            +
                    result = {
         
     | 
| 
      
 38 
     | 
    
         
            +
                      isError: true,
         
     | 
| 
      
 39 
     | 
    
         
            +
                      content: @contents.map(&:to_h)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    }
         
     | 
| 
      
 41 
     | 
    
         
            +
                    result[:structuredContent] = @structured_content if @structured_content
         
     | 
| 
      
 42 
     | 
    
         
            +
                    result
         
     | 
| 
      
 43 
     | 
    
         
            +
                  else
         
     | 
| 
      
 44 
     | 
    
         
            +
                    super
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
       27 
48 
     | 
    
         
             
                # Implementation of build_success_hash for ToolResponse
         
     | 
| 
       28 
49 
     | 
    
         
             
                def build_success_hash
         
     | 
| 
       29 
50 
     | 
    
         
             
                  result = {
         
     | 
| 
         @@ -35,12 +56,14 @@ module ActionMCP 
     | 
|
| 
       35 
56 
     | 
    
         | 
| 
       36 
57 
     | 
    
         
             
                # Implementation of compare_with_same_class for ToolResponse
         
     | 
| 
       37 
58 
     | 
    
         
             
                def compare_with_same_class(other)
         
     | 
| 
       38 
     | 
    
         
            -
                  contents == other.contents && is_error == other.is_error && 
     | 
| 
      
 59 
     | 
    
         
            +
                  contents == other.contents && is_error == other.is_error &&
         
     | 
| 
      
 60 
     | 
    
         
            +
                    structured_content == other.structured_content &&
         
     | 
| 
      
 61 
     | 
    
         
            +
                    tool_execution_error == other.tool_execution_error
         
     | 
| 
       39 
62 
     | 
    
         
             
                end
         
     | 
| 
       40 
63 
     | 
    
         | 
| 
       41 
64 
     | 
    
         
             
                # Implementation of hash_components for ToolResponse
         
     | 
| 
       42 
65 
     | 
    
         
             
                def hash_components
         
     | 
| 
       43 
     | 
    
         
            -
                  [ contents, is_error, structured_content ]
         
     | 
| 
      
 66 
     | 
    
         
            +
                  [ contents, is_error, structured_content, tool_execution_error ]
         
     | 
| 
       44 
67 
     | 
    
         
             
                end
         
     | 
| 
       45 
68 
     | 
    
         | 
| 
       46 
69 
     | 
    
         
             
                # Pretty print for better debugging
         
     | 
| 
         @@ -26,8 +26,13 @@ module ActionMCP 
     | 
|
| 
       26 
26 
     | 
    
         
             
                    # Handle parameter validation errors
         
     | 
| 
       27 
27 
     | 
    
         
             
                    error_response(:invalid_params, message: e.message)
         
     | 
| 
       28 
28 
     | 
    
         
             
                  rescue StandardError => e
         
     | 
| 
       29 
     | 
    
         
            -
                    #  
     | 
| 
       30 
     | 
    
         
            -
                     
     | 
| 
      
 29 
     | 
    
         
            +
                    # Hide error details in production to prevent information disclosure
         
     | 
| 
      
 30 
     | 
    
         
            +
                    message = if Rails.env.production?
         
     | 
| 
      
 31 
     | 
    
         
            +
                      "Tool execution failed"
         
     | 
| 
      
 32 
     | 
    
         
            +
                    else
         
     | 
| 
      
 33 
     | 
    
         
            +
                      "Tool execution failed: #{e.message}"
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                    error_response(:invalid_params, message: message)
         
     | 
| 
       31 
36 
     | 
    
         
             
                  end
         
     | 
| 
       32 
37 
     | 
    
         | 
| 
       33 
38 
     | 
    
         
             
                  def item_klass
         
     | 
    
        data/lib/action_mcp/version.rb
    CHANGED
    
    
    
        data/lib/action_mcp.rb
    CHANGED
    
    
| 
         @@ -32,8 +32,19 @@ class <%= class_name %> < ApplicationMCPTool 
     | 
|
| 
       32 
32 
     | 
    
         
             
                <% end %>
         
     | 
| 
       33 
33 
     | 
    
         
             
                <% end %>
         
     | 
| 
       34 
34 
     | 
    
         | 
| 
      
 35 
     | 
    
         
            +
              # Uncomment to allow additional properties beyond those defined above:
         
     | 
| 
      
 36 
     | 
    
         
            +
              # additional_properties true           # Allow any additional properties
         
     | 
| 
      
 37 
     | 
    
         
            +
              # additional_properties false          # Explicitly disallow additional properties 
         
     | 
| 
      
 38 
     | 
    
         
            +
              # additional_properties({"type" => "string"})  # Allow additional properties but restrict to strings
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       35 
40 
     | 
    
         
             
              def perform
         
     | 
| 
       36 
41 
     | 
    
         
             
                render(text: "Processing <%= properties.map { |p| p[:name] }.join(', ') %>")
         
     | 
| 
      
 42 
     | 
    
         
            +
                
         
     | 
| 
      
 43 
     | 
    
         
            +
                # If additional_properties is enabled, you can access extra parameters:
         
     | 
| 
      
 44 
     | 
    
         
            +
                # extra_params = additional_params
         
     | 
| 
      
 45 
     | 
    
         
            +
                # extra_params.each do |key, value|
         
     | 
| 
      
 46 
     | 
    
         
            +
                #   render(text: "Additional #{key}: #{value}")
         
     | 
| 
      
 47 
     | 
    
         
            +
                # end
         
     | 
| 
       37 
48 
     | 
    
         | 
| 
       38 
49 
     | 
    
         
             
                # Optional outputs:
         
     | 
| 
       39 
50 
     | 
    
         
             
                # render(audio: "<base64_data>", mime_type: "audio/mpeg")
         
     | 
| 
         @@ -41,6 +52,6 @@ class <%= class_name %> < ApplicationMCPTool 
     | 
|
| 
       41 
52 
     | 
    
         
             
                # render(resource: "file://path", mime_type: "application/json", text: "{}")
         
     | 
| 
       42 
53 
     | 
    
         
             
                # render(resource: "file://path", mime_type: "application/octet-stream", blob: "<base64_data>")
         
     | 
| 
       43 
54 
     | 
    
         
             
              rescue => e
         
     | 
| 
       44 
     | 
    
         
            -
                 
     | 
| 
      
 55 
     | 
    
         
            +
                report_error("Error: #{e.message}")
         
     | 
| 
       45 
56 
     | 
    
         
             
              end
         
     | 
| 
       46 
57 
     | 
    
         
             
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: actionmcp
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.83.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Abdelkader Boudih
         
     | 
| 
         @@ -15,14 +15,14 @@ dependencies: 
     | 
|
| 
       15 
15 
     | 
    
         
             
                requirements:
         
     | 
| 
       16 
16 
     | 
    
         
             
                - - ">="
         
     | 
| 
       17 
17 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       18 
     | 
    
         
            -
                    version: 8.0. 
     | 
| 
      
 18 
     | 
    
         
            +
                    version: 8.0.4
         
     | 
| 
       19 
19 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       20 
20 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       21 
21 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       22 
22 
     | 
    
         
             
                requirements:
         
     | 
| 
       23 
23 
     | 
    
         
             
                - - ">="
         
     | 
| 
       24 
24 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       25 
     | 
    
         
            -
                    version: 8.0. 
     | 
| 
      
 25 
     | 
    
         
            +
                    version: 8.0.4
         
     | 
| 
       26 
26 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       27 
27 
     | 
    
         
             
              name: concurrent-ruby
         
     | 
| 
       28 
28 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -71,14 +71,14 @@ dependencies: 
     | 
|
| 
       71 
71 
     | 
    
         
             
                requirements:
         
     | 
| 
       72 
72 
     | 
    
         
             
                - - ">="
         
     | 
| 
       73 
73 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       74 
     | 
    
         
            -
                    version: 8.0. 
     | 
| 
      
 74 
     | 
    
         
            +
                    version: 8.0.4
         
     | 
| 
       75 
75 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       76 
76 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       77 
77 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       78 
78 
     | 
    
         
             
                requirements:
         
     | 
| 
       79 
79 
     | 
    
         
             
                - - ">="
         
     | 
| 
       80 
80 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       81 
     | 
    
         
            -
                    version: 8.0. 
     | 
| 
      
 81 
     | 
    
         
            +
                    version: 8.0.4
         
     | 
| 
       82 
82 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       83 
83 
     | 
    
         
             
              name: zeitwerk
         
     | 
| 
       84 
84 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -93,20 +93,6 @@ dependencies: 
     | 
|
| 
       93 
93 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       94 
94 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       95 
95 
     | 
    
         
             
                    version: '2.6'
         
     | 
| 
       96 
     | 
    
         
            -
            - !ruby/object:Gem::Dependency
         
     | 
| 
       97 
     | 
    
         
            -
              name: ostruct
         
     | 
| 
       98 
     | 
    
         
            -
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       99 
     | 
    
         
            -
                requirements:
         
     | 
| 
       100 
     | 
    
         
            -
                - - ">="
         
     | 
| 
       101 
     | 
    
         
            -
                  - !ruby/object:Gem::Version
         
     | 
| 
       102 
     | 
    
         
            -
                    version: '0'
         
     | 
| 
       103 
     | 
    
         
            -
              type: :runtime
         
     | 
| 
       104 
     | 
    
         
            -
              prerelease: false
         
     | 
| 
       105 
     | 
    
         
            -
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       106 
     | 
    
         
            -
                requirements:
         
     | 
| 
       107 
     | 
    
         
            -
                - - ">="
         
     | 
| 
       108 
     | 
    
         
            -
                  - !ruby/object:Gem::Version
         
     | 
| 
       109 
     | 
    
         
            -
                    version: '0'
         
     | 
| 
       110 
96 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       111 
97 
     | 
    
         
             
              name: json_schemer
         
     | 
| 
       112 
98 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -206,6 +192,12 @@ files: 
     | 
|
| 
       206 
192 
     | 
    
         
             
            - lib/action_mcp/json_rpc_handler_base.rb
         
     | 
| 
       207 
193 
     | 
    
         
             
            - lib/action_mcp/log_subscriber.rb
         
     | 
| 
       208 
194 
     | 
    
         
             
            - lib/action_mcp/logging.rb
         
     | 
| 
      
 195 
     | 
    
         
            +
            - lib/action_mcp/logging/level.rb
         
     | 
| 
      
 196 
     | 
    
         
            +
            - lib/action_mcp/logging/logger.rb
         
     | 
| 
      
 197 
     | 
    
         
            +
            - lib/action_mcp/logging/mixin.rb
         
     | 
| 
      
 198 
     | 
    
         
            +
            - lib/action_mcp/logging/null_logger.rb
         
     | 
| 
      
 199 
     | 
    
         
            +
            - lib/action_mcp/logging/state.rb
         
     | 
| 
      
 200 
     | 
    
         
            +
            - lib/action_mcp/output_schema_builder.rb
         
     | 
| 
       209 
201 
     | 
    
         
             
            - lib/action_mcp/prompt.rb
         
     | 
| 
       210 
202 
     | 
    
         
             
            - lib/action_mcp/prompt_response.rb
         
     | 
| 
       211 
203 
     | 
    
         
             
            - lib/action_mcp/prompts_registry.rb
         
     | 
| 
         @@ -216,6 +208,7 @@ files: 
     | 
|
| 
       216 
208 
     | 
    
         
             
            - lib/action_mcp/resource_response.rb
         
     | 
| 
       217 
209 
     | 
    
         
             
            - lib/action_mcp/resource_template.rb
         
     | 
| 
       218 
210 
     | 
    
         
             
            - lib/action_mcp/resource_templates_registry.rb
         
     | 
| 
      
 211 
     | 
    
         
            +
            - lib/action_mcp/schema_helpers.rb
         
     | 
| 
       219 
212 
     | 
    
         
             
            - lib/action_mcp/server.rb
         
     | 
| 
       220 
213 
     | 
    
         
             
            - lib/action_mcp/server/active_record_session_store.rb
         
     | 
| 
       221 
214 
     | 
    
         
             
            - lib/action_mcp/server/base_messaging.rb
         
     | 
| 
         @@ -226,6 +219,7 @@ files: 
     | 
|
| 
       226 
219 
     | 
    
         
             
            - lib/action_mcp/server/elicitation.rb
         
     | 
| 
       227 
220 
     | 
    
         
             
            - lib/action_mcp/server/error_aware.rb
         
     | 
| 
       228 
221 
     | 
    
         
             
            - lib/action_mcp/server/error_handling.rb
         
     | 
| 
      
 222 
     | 
    
         
            +
            - lib/action_mcp/server/handlers/logging_handler.rb
         
     | 
| 
       229 
223 
     | 
    
         
             
            - lib/action_mcp/server/handlers/prompt_handler.rb
         
     | 
| 
       230 
224 
     | 
    
         
             
            - lib/action_mcp/server/handlers/resource_handler.rb
         
     | 
| 
       231 
225 
     | 
    
         
             
            - lib/action_mcp/server/handlers/router.rb
         
     |