Class: ActionMCP::ResourceTemplate

Inherits:
Object
  • Object
show all
Includes:
CurrentHelpers, ResourceCallbacks, UriAmbiguityChecker, ActiveModel::Model, ActiveModel::Validations
Defined in:
lib/action_mcp/resource_template.rb

Direct Known Subclasses

ApplicationMCPResTemplate

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}) ⇒ ResourceTemplate

Initialize with attribute values



302
303
304
305
306
# File 'lib/action_mcp/resource_template.rb', line 302

def initialize(attributes = {})
  super(attributes)
  @execution_context = {}
  validate!
end

Class Attribute Details

._metaObject (readonly)

Returns the value of attribute _meta.



22
23
24
# File 'lib/action_mcp/resource_template.rb', line 22

def _meta
  @_meta
end

.description(value = nil) ⇒ Object (readonly)

Returns the value of attribute description.



22
23
24
# File 'lib/action_mcp/resource_template.rb', line 22

def description
  @description
end

.mime_type(value = nil) ⇒ Object (readonly)

Returns the value of attribute mime_type.



22
23
24
# File 'lib/action_mcp/resource_template.rb', line 22

def mime_type
  @mime_type
end

.parametersObject (readonly)

Returns the value of attribute parameters.



22
23
24
# File 'lib/action_mcp/resource_template.rb', line 22

def parameters
  @parameters
end

.registered_templatesObject (readonly)

Returns the value of attribute registered_templates.



22
23
24
# File 'lib/action_mcp/resource_template.rb', line 22

def registered_templates
  @registered_templates
end

.template_name(value = nil) ⇒ Object (readonly)

Returns the value of attribute template_name.



22
23
24
# File 'lib/action_mcp/resource_template.rb', line 22

def template_name
  @template_name
end

.uri_template(value = nil) ⇒ Object (readonly)

Returns the value of attribute uri_template.



22
23
24
# File 'lib/action_mcp/resource_template.rb', line 22

def uri_template
  @uri_template
end

Instance Attribute Details

#execution_contextObject (readonly)

Returns the value of attribute execution_context.



16
17
18
# File 'lib/action_mcp/resource_template.rb', line 16

def execution_context
  @execution_context
end

Class Method Details

.abstract!Object



29
30
31
32
33
34
35
# File 'lib/action_mcp/resource_template.rb', line 29

def abstract!
  @abstract = true
  # Unregister from the appropriate registry if already registered
  return unless ActionMCP::ResourceTemplatesRegistry.items.values.include?(self)

  ActionMCP::ResourceTemplatesRegistry.unregister(self)
end

.abstract?Boolean

Returns:

  • (Boolean)


25
26
27
# File 'lib/action_mcp/resource_template.rb', line 25

def abstract?
  @abstract ||= false
end

.build_resource(uri:, name:, title: nil, description: nil, mime_type: nil, size: nil, annotations: nil, meta: nil) ⇒ ActionMCP::Resource

Factory helper that fills in template-level defaults.

Parameters:

  • uri (String)

    The concrete resource URI

  • name (String)

    Display name

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

    Human-readable title

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

    Falls back to template description

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

    Falls back to template mime_type

  • size (Integer, nil) (defaults to: nil)

    Size in bytes

  • annotations (Hash, nil) (defaults to: nil)

    Optional annotations

  • meta (Hash, #to_hash, #to_h, nil) (defaults to: nil)

    Optional extension metadata passed through to the Resource (emitted as ‘_meta`)

Returns:



161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/action_mcp/resource_template.rb', line 161

def build_resource(uri:, name:, title: nil, description: nil, mime_type: nil, size: nil, annotations: nil, meta: nil)
  ActionMCP::Resource.new(
    uri: uri,
    name: name,
    title: title,
    description: description || @description,
    mime_type: mime_type || @mime_type,
    size: size,
    annotations: annotations,
    meta: meta
  )
end

.capability_nameObject



128
129
130
131
132
# File 'lib/action_mcp/resource_template.rb', line 128

def capability_name
  return "" if name.nil?

  @capability_name ||= name.demodulize.underscore.sub(/_template$/, "")
end

.inherited(subclass) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/action_mcp/resource_template.rb', line 37

def inherited(subclass)
  super
  subclass.instance_variable_set(:@abstract, false)
  # Create a copy of validation requirements for subclasses
  subclass.instance_variable_set(:@required_parameters, [])

  # Run the ActiveSupport load hook when a resource template is defined
  subclass.class_eval do
    ActiveSupport.run_load_hooks(:action_mcp_resource_template, subclass)
  end
end

.list(session: nil) ⇒ Array<ActionMCP::Resource>

Override in subclasses to enumerate concrete resources. Returns Array<ActionMCP::Resource>.

Parameters:

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

    The current MCP session

Returns:



141
142
143
# File 'lib/action_mcp/resource_template.rb', line 141

def list(session: nil)
  []
end

.lists_resources?Boolean

Returns true if this template subclass overrides list.

Returns:

  • (Boolean)


146
147
148
# File 'lib/action_mcp/resource_template.rb', line 146

def lists_resources?
  method(:list).owner != ActionMCP::ResourceTemplate.singleton_class
end

.meta(data = nil) ⇒ Object

Sets or retrieves the _meta field



101
102
103
104
105
106
107
108
109
110
# File 'lib/action_mcp/resource_template.rb', line 101

def meta(data = nil)
  if data
    raise ArgumentError, "_meta must be a hash" unless data.is_a?(Hash)

    @_meta ||= {}
    @_meta = @_meta.merge(data)
  else
    @_meta || {}
  end
end

.parameter(name, description:, required: false, **options) ⇒ Object Also known as: attribute



59
60
61
62
63
64
65
66
67
68
# File 'lib/action_mcp/resource_template.rb', line 59

def parameter(name, description:, required: false, **options)
  @parameters ||= {}
  @parameters[name] = { description: description, required: required, **options }

  # Define attribute accessor if not already defined
  attr_accessor name unless method_defined?(name) && method_defined?("#{name}=")

  # Track required parameters for validation
  required_parameters << name if required
end

.process(uri_string) ⇒ Object

Process a URI string to create a template instance



191
192
193
194
195
196
197
198
199
200
# File 'lib/action_mcp/resource_template.rb', line 191

def process(uri_string)
  return nil unless @uri_template

  # Extract parameters from URI using pattern matching
  params = extract_params_from_uri(uri_string)
  return new if params.nil? # Return invalid template for bad URI

  # Create new instance with the extracted parameters
  new(params)
end

.readable_uri?(uri) ⇒ Boolean

Check if a concrete URI is readable by this template. Returns false if the URI doesn’t match the template pattern.

Parameters:

  • uri (String)

    A concrete URI to check

Returns:

  • (Boolean)


179
180
181
182
183
184
185
186
187
188
# File 'lib/action_mcp/resource_template.rb', line 179

def readable_uri?(uri)
  return false unless @uri_template

  params = extract_params_from_uri(uri)
  return false if params.nil?

  new(params).valid?
rescue StandardError
  false
end

.required_parametersObject

Track required parameters for validation



50
51
52
# File 'lib/action_mcp/resource_template.rb', line 50

def required_parameters
  @required_parameters ||= []
end

.to_hObject



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/action_mcp/resource_template.rb', line 112

def to_h
  name_value = defined?(@template_name) ? @template_name : name.demodulize.underscore.gsub(/_template$/, "")

  result = {
    uriTemplate: @uri_template,
    name: name_value,
    description: @description,
    mimeType: @mime_type
  }.compact

  # Add _meta if present
  result[:_meta] = @_meta if @_meta&.any?

  result
end

.uri_regex_cacheObject

Cache compiled regex patterns for URI matching to avoid recompilation



55
56
57
# File 'lib/action_mcp/resource_template.rb', line 55

def uri_regex_cache
  @uri_regex_cache ||= {}
end

Instance Method Details

#callObject



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/action_mcp/resource_template.rb', line 329

def call
  @response = ResourceResponse.new

  # Validate parameters first
  unless valid?
    missing_params = errors.full_messages
    @response.mark_as_parameter_validation_failed!(missing_params, "template://#{self.class.name}")
    return @response
  end

  begin
    run_callbacks :resolve do
      result = resolve
      if result.nil?
        @response.mark_as_not_found!("template://#{self.class.name}")
      else
        @response.add_content(result)
      end
      @response
    end
  rescue StandardError => e
    @response.mark_as_resolution_failed!("template://#{self.class.name}", e.message)
  end

  @response
end

#sessionObject



318
319
320
# File 'lib/action_mcp/resource_template.rb', line 318

def session
  execution_context[:session]
end

#validate!Object

Override validate! to not raise exceptions



309
310
311
# File 'lib/action_mcp/resource_template.rb', line 309

def validate!
  valid?
end

#with_context(context) ⇒ Object



313
314
315
316
# File 'lib/action_mcp/resource_template.rb', line 313

def with_context(context)
  @execution_context = context
  self
end