Class: ActionMCP::ResourceTemplate
- Inherits:
-
Object
- Object
- ActionMCP::ResourceTemplate
- Includes:
- CurrentHelpers, ResourceCallbacks, UriAmbiguityChecker, ActiveModel::Model, ActiveModel::Validations
- Defined in:
- lib/action_mcp/resource_template.rb
Direct Known Subclasses
Class Attribute Summary collapse
-
._meta ⇒ Object
readonly
Returns the value of attribute _meta.
-
.description(value = nil) ⇒ Object
readonly
Returns the value of attribute description.
-
.mime_type(value = nil) ⇒ Object
readonly
Returns the value of attribute mime_type.
-
.parameters ⇒ Object
readonly
Returns the value of attribute parameters.
-
.registered_templates ⇒ Object
readonly
Returns the value of attribute registered_templates.
-
.template_name(value = nil) ⇒ Object
readonly
Returns the value of attribute template_name.
-
.ui_meta ⇒ Object
readonly
Returns the value of attribute ui_meta.
-
.uri_template(value = nil) ⇒ Object
readonly
Returns the value of attribute uri_template.
Instance Attribute Summary collapse
-
#execution_context ⇒ Object
readonly
Returns the value of attribute execution_context.
-
#resolved_uri ⇒ Object
readonly
Returns the value of attribute resolved_uri.
Class Method Summary collapse
- .abstract! ⇒ Object
- .abstract? ⇒ Boolean
- .addressable_template_cache ⇒ Object
-
.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.
- .capability_name ⇒ Object
- .inherited(subclass) ⇒ Object
-
.list(session: nil) ⇒ Array<ActionMCP::Resource>
Override in subclasses to enumerate concrete resources.
-
.lists_resources? ⇒ Boolean
Returns true if this template subclass overrides
list. -
.meta(data = nil) ⇒ Object
Sets or retrieves the _meta field.
- .meta_with_ui(meta = nil) ⇒ Object
- .parameter(name, description:, required: false, **options) ⇒ Object (also: attribute)
-
.process(uri_string) ⇒ Object
Process a URI string to create a template instance.
-
.readable_uri?(uri) ⇒ Boolean
Check if a concrete URI is readable by this template.
-
.required_parameters ⇒ Object
Track required parameters for validation.
- .to_h ⇒ Object
-
.ui(**data) ⇒ Object
Declares MCP Apps UI metadata for this resource template.
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(attributes = {}) ⇒ ResourceTemplate
constructor
Initialize with attribute values.
-
#render_ui(text: nil, template: nil, layout: false, locals: {}, meta: nil) ⇒ Object
Build a
Content::Resourcefor an MCP Apps UI view. - #session ⇒ Object
-
#validate! ⇒ Object
Override validate! to not raise exceptions.
- #with_context(context) ⇒ Object
Constructor Details
#initialize(attributes = {}) ⇒ ResourceTemplate
Initialize with attribute values
438 439 440 441 442 |
# File 'lib/action_mcp/resource_template.rb', line 438 def initialize(attributes = {}) super(attributes) @execution_context = {} validate! end |
Class Attribute Details
._meta ⇒ Object (readonly)
Returns the value of attribute _meta.
24 25 26 |
# File 'lib/action_mcp/resource_template.rb', line 24 def @_meta end |
.description(value = nil) ⇒ Object (readonly)
Returns the value of attribute description.
24 25 26 |
# File 'lib/action_mcp/resource_template.rb', line 24 def description @description end |
.mime_type(value = nil) ⇒ Object (readonly)
Returns the value of attribute mime_type.
24 25 26 |
# File 'lib/action_mcp/resource_template.rb', line 24 def mime_type @mime_type end |
.parameters ⇒ Object (readonly)
Returns the value of attribute parameters.
24 25 26 |
# File 'lib/action_mcp/resource_template.rb', line 24 def parameters @parameters end |
.registered_templates ⇒ Object (readonly)
Returns the value of attribute registered_templates.
24 25 26 |
# File 'lib/action_mcp/resource_template.rb', line 24 def registered_templates @registered_templates end |
.template_name(value = nil) ⇒ Object (readonly)
Returns the value of attribute template_name.
24 25 26 |
# File 'lib/action_mcp/resource_template.rb', line 24 def template_name @template_name end |
.ui_meta ⇒ Object (readonly)
Returns the value of attribute ui_meta.
24 25 26 |
# File 'lib/action_mcp/resource_template.rb', line 24 def @ui_meta end |
.uri_template(value = nil) ⇒ Object (readonly)
Returns the value of attribute uri_template.
24 25 26 |
# File 'lib/action_mcp/resource_template.rb', line 24 def uri_template @uri_template end |
Instance Attribute Details
#execution_context ⇒ Object (readonly)
Returns the value of attribute execution_context.
18 19 20 |
# File 'lib/action_mcp/resource_template.rb', line 18 def execution_context @execution_context end |
#resolved_uri ⇒ Object (readonly)
Returns the value of attribute resolved_uri.
18 19 20 |
# File 'lib/action_mcp/resource_template.rb', line 18 def resolved_uri @resolved_uri end |
Class Method Details
.abstract! ⇒ Object
31 32 33 34 35 36 37 |
# File 'lib/action_mcp/resource_template.rb', line 31 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
27 28 29 |
# File 'lib/action_mcp/resource_template.rb', line 27 def abstract? @abstract ||= false end |
.addressable_template_cache ⇒ Object
56 57 58 |
# File 'lib/action_mcp/resource_template.rb', line 56 def addressable_template_cache @addressable_template_cache ||= {} 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.
289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/action_mcp/resource_template.rb', line 289 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: () ) end |
.capability_name ⇒ Object
256 257 258 259 260 |
# File 'lib/action_mcp/resource_template.rb', line 256 def capability_name return "" if name.nil? @capability_name ||= name.demodulize.underscore.sub(/_template$/, "") end |
.inherited(subclass) ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/action_mcp/resource_template.rb', line 39 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 ArrayActionMCP::Resource.
269 270 271 |
# File 'lib/action_mcp/resource_template.rb', line 269 def list(session: nil) [] end |
.lists_resources? ⇒ Boolean
Returns true if this template subclass overrides list.
274 275 276 |
# File 'lib/action_mcp/resource_template.rb', line 274 def lists_resources? method(:list).owner != ActionMCP::ResourceTemplate.singleton_class end |
.meta(data = nil) ⇒ Object
Sets or retrieves the _meta field
104 105 106 107 108 109 110 111 112 113 |
# File 'lib/action_mcp/resource_template.rb', line 104 def (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 |
.meta_with_ui(meta = nil) ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/action_mcp/resource_template.rb', line 130 def ( = nil) = () = @ui_meta&.any? ? { ui: @ui_meta } : {} combined = .deep_merge() return nil if combined.empty? ui_present, combined_ui = (combined, :ui) (combined_ui) if ui_present combined end |
.parameter(name, description:, required: false, **options) ⇒ Object Also known as: attribute
60 61 62 63 64 65 66 67 68 69 |
# File 'lib/action_mcp/resource_template.rb', line 60 def parameter(name, description:, required: false, **) @parameters ||= {} @parameters[name] = { description: description, required: required, ** } # 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
319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/action_mcp/resource_template.rb', line 319 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 # Preserve the concrete URI requested by the client. The template URI # may contain placeholders and must not leak into resource contents. new(params).tap { |record| record.instance_variable_set(:@resolved_uri, uri_string) } 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.
307 308 309 310 311 312 313 314 315 316 |
# File 'lib/action_mcp/resource_template.rb', line 307 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_parameters ⇒ Object
Track required parameters for validation
52 53 54 |
# File 'lib/action_mcp/resource_template.rb', line 52 def required_parameters @required_parameters ||= [] end |
.to_h ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/action_mcp/resource_template.rb', line 240 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 = (@_meta) result[:_meta] = if &.any? result end |
.ui(**data) ⇒ Object
Declares MCP Apps UI metadata for this resource template. Stored verbatim
(camelCase keys per the ext-apps spec). Used both for the resources/list
entry's _meta.ui and the default content _meta.ui produced by
render_ui.
122 123 124 125 126 127 128 |
# File 'lib/action_mcp/resource_template.rb', line 122 def ui(**data) raise ArgumentError, "ui metadata must not be empty" if data.empty? (data) @ui_meta ||= {} @ui_meta = @ui_meta.deep_merge(data) end |
Instance Method Details
#call ⇒ Object
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 |
# File 'lib/action_mcp/resource_template.rb', line 465 def call @response = ResourceResponse.new # Validate parameters first unless valid? missing_params = errors. @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.) end @response end |
#render_ui(text: nil, template: nil, layout: false, locals: {}, meta: nil) ⇒ Object
Build a Content::Resource for an MCP Apps UI view. Accepts either a raw
:text string or a Rails :template path; the class-level ui macro
supplies the content-level _meta.ui automatically.
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/action_mcp/resource_template.rb', line 392 def render_ui(text: nil, template: nil, layout: false, locals: {}, meta: nil) raise ArgumentError, "render_ui accepts either :text or :template, not both" if !text.nil? && !template.nil? resolved = if !text.nil? text elsif !template.nil? rendered = ActionMCP::MCPAppRenderer.render(template: template, layout: layout, locals: locals) if rendered.to_s.strip.empty? ActionMCP.logger.warn( "[ActionMCP] render_ui produced empty output for #{self.class.name} " \ "(uri_template=#{self.class.uri_template.inspect}, template=#{template.inspect}). " \ "Check the template path and host view configuration." ) end rendered else raise ArgumentError, "render_ui requires :text or :template" end resource_uri = resolved_uri || self.class.uri_template unless resource_uri.is_a?(String) && ActionMCP::Apps::URI_SCHEME.match?(resource_uri) raise ArgumentError, "render_ui requires a concrete ui:// resource URI, got: #{resource_uri.inspect}" end if resolved_uri.nil? && resource_uri.match?(/\{[^}]+\}/) raise ArgumentError, "render_ui cannot emit a parameterized URI template without a concrete request URI" end resource_mime_type = self.class.mime_type || ActionMCP::Apps::MIME_TYPE unless resource_mime_type == ActionMCP::Apps::MIME_TYPE raise ArgumentError, "render_ui requires mime type #{ActionMCP::Apps::MIME_TYPE.inspect}, got: #{resource_mime_type.inspect}" end unless resolved.is_a?(String) raise ArgumentError, "render_ui content must be a string, got: #{resolved.class}" end ActionMCP::Content::Resource.new( resource_uri, resource_mime_type, text: resolved, meta: self.class.() ) end |
#session ⇒ Object
454 455 456 |
# File 'lib/action_mcp/resource_template.rb', line 454 def session execution_context[:session] end |
#validate! ⇒ Object
Override validate! to not raise exceptions
445 446 447 |
# File 'lib/action_mcp/resource_template.rb', line 445 def validate! valid? end |
#with_context(context) ⇒ Object
449 450 451 452 |
# File 'lib/action_mcp/resource_template.rb', line 449 def with_context(context) @execution_context = context self end |