actionmcp 0.201.0 → 0.201.2
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/lib/action_mcp/apps/view_manifest.rb +3 -3
- data/lib/action_mcp/configuration.rb +45 -8
- data/lib/action_mcp/engine.rb +3 -3
- data/lib/action_mcp/prompt.rb +1 -1
- data/lib/action_mcp/registry_base.rb +15 -7
- data/lib/action_mcp/resource_template.rb +1 -1
- data/lib/action_mcp/tool.rb +1 -1
- data/lib/action_mcp/tool_response.rb +4 -1
- data/lib/action_mcp/version.rb +1 -1
- data/lib/action_mcp.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 49402a02736231ce65b7851e94d98550eb0dac2b605d8e469b4907ae886677db
|
|
4
|
+
data.tar.gz: 69432a9e6a7ccfaf557a3eca2fc44136d3f2a658fa46bb30b9c0e119cf9cde2a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7687741dd8be7cdeac36a500154dfd2a9441c352ce0c2be1524f3dedbfe4c75ada593022e82f933b316070f73ffac112fa0c7f291ba67d24f335784cc187f1bd
|
|
7
|
+
data.tar.gz: 87a0ae3173f26f09a0e323e24649b691341683ec77dbd1ab1602dcca15eb7570bba043150c8c9764c21dad6a27ca9ee6178b73f622f2231eb424b31625191424
|
|
@@ -82,13 +82,13 @@ module ActionMCP
|
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
generated.concat(ResourceTemplate.registered_templates.select { |klass| generated_template?(klass) })
|
|
85
|
-
generated.concat(ResourceTemplatesRegistry.
|
|
85
|
+
generated.concat(ResourceTemplatesRegistry.store.values.select { |klass| generated_template?(klass) })
|
|
86
86
|
generated.uniq!
|
|
87
87
|
|
|
88
88
|
ResourceTemplate.registered_templates.delete_if do |klass|
|
|
89
89
|
generated.include?(klass) || generated_template?(klass)
|
|
90
90
|
end
|
|
91
|
-
ResourceTemplatesRegistry.
|
|
91
|
+
ResourceTemplatesRegistry.store.delete_if do |_name, klass|
|
|
92
92
|
generated.include?(klass) || generated_template?(klass)
|
|
93
93
|
end
|
|
94
94
|
|
|
@@ -139,7 +139,7 @@ module ActionMCP
|
|
|
139
139
|
end
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
ResourceTemplatesRegistry.
|
|
142
|
+
ResourceTemplatesRegistry.store.delete_if { |_key, registered| registered.equal?(klass) }
|
|
143
143
|
ResourceTemplatesRegistry.register(klass)
|
|
144
144
|
|
|
145
145
|
[ klass, logical_uri, uri ]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "monitor"
|
|
3
4
|
require_relative "gateway"
|
|
4
5
|
|
|
5
6
|
module ActionMCP
|
|
@@ -107,6 +108,8 @@ module ActionMCP
|
|
|
107
108
|
# Browser origins allowed to reach a local server. Remote deployments must
|
|
108
109
|
# explicitly add their trusted origin hosts.
|
|
109
110
|
@allowed_origins = %w[localhost 127.0.0.1 ::1].freeze
|
|
111
|
+
|
|
112
|
+
@mcp_components_monitor = Monitor.new
|
|
110
113
|
end
|
|
111
114
|
|
|
112
115
|
def name
|
|
@@ -340,21 +343,55 @@ module ActionMCP
|
|
|
340
343
|
end
|
|
341
344
|
|
|
342
345
|
def eager_load_if_needed
|
|
343
|
-
|
|
344
|
-
|
|
346
|
+
ensure_mcp_components_loaded if mcp_components_load_needed?
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Loading app/mcp inside initialize! drags every model the tools reference into boot.
|
|
350
|
+
def prepare_mcp_components
|
|
351
|
+
if defer_mcp_components_load?
|
|
352
|
+
@mcp_components_state = (:deferred if mcp_components_load_needed?)
|
|
353
|
+
else
|
|
354
|
+
@mcp_components_state = nil
|
|
355
|
+
eager_load_if_needed
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def mcp_components_deferred?
|
|
360
|
+
@mcp_components_state == :deferred
|
|
361
|
+
end
|
|
345
362
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
profile[:resources]&.include?("all")
|
|
363
|
+
# Reentrant Monitor: the load registers (and may read) through the registries on this thread.
|
|
364
|
+
def load_deferred_mcp_components
|
|
365
|
+
return unless @mcp_components_state
|
|
350
366
|
|
|
351
|
-
|
|
367
|
+
@mcp_components_monitor.synchronize do
|
|
368
|
+
next unless @mcp_components_state == :deferred
|
|
352
369
|
|
|
353
|
-
|
|
370
|
+
@mcp_components_state = :loading
|
|
371
|
+
begin
|
|
372
|
+
ensure_mcp_components_loaded
|
|
373
|
+
@mcp_components_state = nil
|
|
374
|
+
ensure
|
|
375
|
+
@mcp_components_state = :deferred if @mcp_components_state == :loading
|
|
376
|
+
end
|
|
377
|
+
end
|
|
354
378
|
end
|
|
355
379
|
|
|
356
380
|
private
|
|
357
381
|
|
|
382
|
+
def defer_mcp_components_load?
|
|
383
|
+
!Rails.configuration.eager_load && !Rails.application.initialized?
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def mcp_components_load_needed?
|
|
387
|
+
profile = @profiles[active_profile]
|
|
388
|
+
return false unless profile
|
|
389
|
+
|
|
390
|
+
profile[:tools]&.include?("all") ||
|
|
391
|
+
profile[:prompts]&.include?("all") ||
|
|
392
|
+
profile[:resources]&.include?("all")
|
|
393
|
+
end
|
|
394
|
+
|
|
358
395
|
def default_profiles
|
|
359
396
|
{
|
|
360
397
|
primary: {
|
data/lib/action_mcp/engine.rb
CHANGED
|
@@ -50,9 +50,9 @@ module ActionMCP
|
|
|
50
50
|
ActionMCP::PromptsRegistry.clear!
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
# Eager load MCP components if profile includes "all"
|
|
54
|
-
#
|
|
55
|
-
ActionMCP.configuration.
|
|
53
|
+
# Eager load MCP components if profile includes "all". Booting without
|
|
54
|
+
# eager loading defers them to the first registry read instead.
|
|
55
|
+
ActionMCP.configuration.prepare_mcp_components
|
|
56
56
|
|
|
57
57
|
# Register compiled MCP Apps views after registries
|
|
58
58
|
# are (re)built. Runs on each dev reload so rebuilt view bundles/hashes
|
data/lib/action_mcp/prompt.rb
CHANGED
|
@@ -56,7 +56,7 @@ module ActionMCP
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def unregister_from_registry
|
|
59
|
-
ActionMCP::PromptsRegistry.unregister(self) if ActionMCP::PromptsRegistry.
|
|
59
|
+
ActionMCP::PromptsRegistry.unregister(self) if ActionMCP::PromptsRegistry.store.values.include?(self)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
# Hook called when a class inherits from Prompt
|
|
@@ -7,13 +7,21 @@ module ActionMCP
|
|
|
7
7
|
class NotFound < StandardError; end
|
|
8
8
|
|
|
9
9
|
class << self
|
|
10
|
-
#
|
|
10
|
+
# Raw storage for writes: registering never triggers the deferred component load.
|
|
11
11
|
#
|
|
12
12
|
# @return [Hash] A hash of registered items.
|
|
13
|
-
def
|
|
13
|
+
def store
|
|
14
14
|
@items ||= {}
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
# Returns all registered items, running a deferred component load first.
|
|
18
|
+
#
|
|
19
|
+
# @return [Hash] A hash of registered items.
|
|
20
|
+
def items
|
|
21
|
+
ActionMCP.configuration.load_deferred_mcp_components
|
|
22
|
+
store
|
|
23
|
+
end
|
|
24
|
+
|
|
17
25
|
# Register an item explicitly
|
|
18
26
|
#
|
|
19
27
|
# @param klass [Class] The class to register
|
|
@@ -22,7 +30,7 @@ module ActionMCP
|
|
|
22
30
|
return if klass.abstract?
|
|
23
31
|
|
|
24
32
|
name = klass.capability_name
|
|
25
|
-
|
|
33
|
+
store[name] = klass
|
|
26
34
|
klass._registered_name = name if klass.respond_to?(:_registered_name=)
|
|
27
35
|
end
|
|
28
36
|
|
|
@@ -38,11 +46,11 @@ module ActionMCP
|
|
|
38
46
|
new_name = klass.capability_name
|
|
39
47
|
|
|
40
48
|
# Remove old entry if it exists and points to this class
|
|
41
|
-
if old_name &&
|
|
42
|
-
|
|
49
|
+
if old_name && store[old_name] == klass
|
|
50
|
+
store.delete(old_name)
|
|
43
51
|
end
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
store[new_name] = klass
|
|
46
54
|
klass._registered_name = new_name if klass.respond_to?(:_registered_name=)
|
|
47
55
|
end
|
|
48
56
|
|
|
@@ -51,7 +59,7 @@ module ActionMCP
|
|
|
51
59
|
# @param klass [Class] The class to unregister
|
|
52
60
|
# @return [void]
|
|
53
61
|
def unregister(klass)
|
|
54
|
-
|
|
62
|
+
store.delete(klass.capability_name)
|
|
55
63
|
end
|
|
56
64
|
|
|
57
65
|
# Retrieve an item by name.
|
|
@@ -31,7 +31,7 @@ module ActionMCP
|
|
|
31
31
|
def abstract!
|
|
32
32
|
@abstract = true
|
|
33
33
|
# Unregister from the appropriate registry if already registered
|
|
34
|
-
return unless ActionMCP::ResourceTemplatesRegistry.
|
|
34
|
+
return unless ActionMCP::ResourceTemplatesRegistry.store.values.include?(self)
|
|
35
35
|
|
|
36
36
|
ActionMCP::ResourceTemplatesRegistry.unregister(self)
|
|
37
37
|
end
|
data/lib/action_mcp/tool.rb
CHANGED
|
@@ -87,7 +87,7 @@ module ActionMCP
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def unregister_from_registry
|
|
90
|
-
ActionMCP::ToolsRegistry.unregister(self) if ActionMCP::ToolsRegistry.
|
|
90
|
+
ActionMCP::ToolsRegistry.unregister(self) if ActionMCP::ToolsRegistry.store.values.include?(self)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
# Hook called when a class inherits from Tool
|
|
@@ -102,7 +102,10 @@ module ActionMCP
|
|
|
102
102
|
raise ArgumentError, "content must respond to #to_h"
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
content.to_h
|
|
105
|
+
result = content.to_h
|
|
106
|
+
# Built-in content validates itself in #to_h; anything else is checked here.
|
|
107
|
+
Content::Validation.validate_content_block!(result) unless content.is_a?(Content::Base)
|
|
108
|
+
result
|
|
106
109
|
end
|
|
107
110
|
end
|
|
108
111
|
end
|
data/lib/action_mcp/version.rb
CHANGED
data/lib/action_mcp.rb
CHANGED
|
@@ -83,14 +83,14 @@ module ActionMCP
|
|
|
83
83
|
#
|
|
84
84
|
# @return [ActionMCP::RegistryBase::RegistryScope] the available tools
|
|
85
85
|
def available_tools
|
|
86
|
-
ToolsRegistry.
|
|
86
|
+
ToolsRegistry.non_abstract
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
# Returns the available prompts.
|
|
90
90
|
#
|
|
91
91
|
# @return [ActionMCP::RegistryBase::RegistryScope] the available prompts
|
|
92
92
|
def available_prompts
|
|
93
|
-
PromptsRegistry.
|
|
93
|
+
PromptsRegistry.non_abstract
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
ActiveModel::Type.register(:string_array, StringArray)
|