actionmcp 0.27.0 → 0.28.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1c71d0883d3ace526d4afda2bb71f53efd68b64b15a28febbc5d82710d8beb5
4
- data.tar.gz: 30c3d4a8f241f52d103ab93544ce147aad7553dae08e60e7a4bccb1d408918aa
3
+ metadata.gz: 644f0795fca70107d86ba512e1c7083535f33269e4a53a488cc10d53d93ffd28
4
+ data.tar.gz: 187ecb0de2249772b758177195b4c6006ff44e296cd518ba0c44f9ab83b58b19
5
5
  SHA512:
6
- metadata.gz: 3f2be84c315a68e574357e54b6500cfe2e1864dcf83a7633113c2e6268f90131d00c318e87cac247b931986085bc99baf020de9845a5733cea3a878bd45f2ee3
7
- data.tar.gz: 64f6ccdcd734bae57d600730eba4058f2b7934e0c62ffef38f58185aa1d1f71102bd6a5be74e8f336e4494524908fcbc2e21ab44af7f08cfe031050834c4d628
6
+ metadata.gz: dee5271d34e57f3efb3b2459f1b258359a9b36040ca36e2567cf989435105322493cd344594a211a0cd4c426800153b5e9d21e7dd7c18a38183d79d593bedbb4
7
+ data.tar.gz: c5c5ecb5398636afec82b781622f130da83aee55c04765eb9a04fd06b444cd10fd4c27b65d7485a9775d771e60605a85c5137323be10234ca9ebfcd0bebd480d
@@ -14,7 +14,7 @@ module ActionMCP
14
14
  # @!attribute resources_subscribe
15
15
  # @return [Boolean] Whether to subscribe to resources.
16
16
  # @!attribute logging_level
17
- # @return [Symbol] The logging level. attr_writer :name, :version
17
+ # @return [Symbol] The logging level.
18
18
  attr_writer :name, :version
19
19
  attr_accessor :logging_enabled,
20
20
  :list_changed,
@@ -40,21 +40,27 @@ module ActionMCP
40
40
  @version || (has_rails_version ? Rails.application.version.to_s : "0.0.1")
41
41
  end
42
42
 
43
+ # Get active profile (considering thread-local override)
44
+ def active_profile
45
+ ActionMCP.thread_profiles.value || @active_profile
46
+ end
47
+
43
48
  # Load custom profiles from Rails configuration
44
49
  def load_profiles
45
50
  # First load defaults from the gem
46
51
  @profiles = default_profiles
47
52
 
48
- # Then try to load from config/mcp.yml in the Rails app
49
- config_path = Rails.root.join("config", "mcp.yml")
50
- if File.exist?(config_path)
51
- begin
52
- yaml_content = YAML.safe_load(File.read(config_path), symbolize_names: true)
53
- # Merge with defaults so user config overrides gem defaults
54
- @profiles.deep_merge!(yaml_content) if yaml_content
55
- rescue StandardError => e
56
- Rails.logger.error "Failed to load MCP profiles from #{config_path}: #{e.message}"
57
- end
53
+ # Try to load from config/mcp.yml in the Rails app using Rails.config_for
54
+ begin
55
+ app_config = Rails.application.config_for(:mcp)
56
+
57
+ raise "Invalid MCP config file" unless app_config.is_a?(Hash)
58
+
59
+ # Merge with defaults so user config overrides gem defaults
60
+ @profiles = app_config
61
+ rescue => e
62
+ # If the config file doesn't exist in the Rails app, just use the defaults
63
+ Rails.logger.debug "No MCP config found in Rails app, using defaults from gem"
58
64
  end
59
65
 
60
66
  # Apply the active profile
@@ -67,8 +73,8 @@ module ActionMCP
67
73
  def use_profile(profile_name)
68
74
  profile_name = profile_name.to_sym
69
75
  unless @profiles.key?(profile_name)
70
- Rails.logger.warn "Profile '#{profile_name}' not found, using default"
71
- profile_name = :default
76
+ Rails.logger.warn "Profile '#{profile_name}' not found, using primary"
77
+ profile_name = :primary
72
78
  end
73
79
 
74
80
  @active_profile = profile_name
@@ -81,7 +87,7 @@ module ActionMCP
81
87
  def filtered_tools
82
88
  return ToolsRegistry.non_abstract if should_include_all?(:tools)
83
89
 
84
- tool_names = @profiles[@active_profile][:tools] || []
90
+ tool_names = @profiles[active_profile][:tools] || []
85
91
  # Convert tool names to underscored format
86
92
  tool_names = tool_names.map { |name| name.to_s.underscore }
87
93
  ToolsRegistry.non_abstract.select { |tool| tool_names.include?(tool.name.underscore) }
@@ -91,7 +97,7 @@ module ActionMCP
91
97
  def filtered_prompts
92
98
  return PromptsRegistry.non_abstract if should_include_all?(:prompts)
93
99
 
94
- prompt_names = @profiles[@active_profile][:prompts] || []
100
+ prompt_names = @profiles[active_profile][:prompts] || []
95
101
  PromptsRegistry.non_abstract.select { |prompt| prompt_names.include?(prompt.name) }
96
102
  end
97
103
 
@@ -99,21 +105,45 @@ module ActionMCP
99
105
  def filtered_resources
100
106
  return ResourceTemplatesRegistry.non_abstract if should_include_all?(:resources)
101
107
 
102
- resource_names = @profiles[@active_profile][:resources] || []
108
+ resource_names = @profiles[active_profile][:resources] || []
103
109
  ResourceTemplatesRegistry.non_abstract.select { |resource| resource_names.include?(resource.name) }
104
110
  end
105
111
 
106
112
  # Returns capabilities based on active profile
107
113
  def capabilities
108
114
  capabilities = {}
109
- # Only include each capability if the corresponding filtered registry is non-empty
110
- capabilities[:tools] = { listChanged: @list_changed } if filtered_tools.any?
111
- capabilities[:prompts] = { listChanged: @list_changed } if filtered_prompts.any?
112
- capabilities[:logging] = {} if @logging_enabled
113
- capabilities[:resources] = { subscribe: @resources_subscribe } if filtered_resources.any?
115
+
116
+ # Only include capabilities if the corresponding filtered registry is non-empty
117
+ if filtered_tools.any?
118
+ capabilities[:tools] = { listChanged: @list_changed }
119
+ end
120
+
121
+ if filtered_prompts.any?
122
+ capabilities[:prompts] = { listChanged: @list_changed }
123
+ end
124
+
125
+ if @logging_enabled
126
+ capabilities[:logging] = {}
127
+ end
128
+
129
+ if filtered_resources.any?
130
+ capabilities[:resources] = { subscribe: @resources_subscribe }
131
+ end
132
+
114
133
  capabilities
115
134
  end
116
135
 
136
+ def apply_profile_options
137
+ profile = @profiles[active_profile]
138
+ return unless profile && profile[:options]
139
+
140
+ options = profile[:options]
141
+ @list_changed = options[:list_changed] unless options[:list_changed].nil?
142
+ @logging_enabled = options[:logging_enabled] unless options[:logging_enabled].nil?
143
+ @logging_level = options[:logging_level] unless options[:logging_level].nil?
144
+ @resources_subscribe = options[:resources_subscribe] unless options[:resources_subscribe].nil?
145
+ end
146
+
117
147
  private
118
148
 
119
149
  def default_profiles
@@ -143,22 +173,12 @@ module ActionMCP
143
173
  }
144
174
  end
145
175
 
146
- def apply_profile_options
147
- profile = @profiles[@active_profile]
148
- return unless profile && profile[:options]
149
-
150
- options = profile[:options]
151
- @list_changed = options[:list_changed] unless options[:list_changed].nil?
152
- @logging_enabled = options[:logging_enabled] unless options[:logging_enabled].nil?
153
- @logging_level = options[:logging_level] unless options[:logging_level].nil?
154
- @resources_subscribe = options[:resources_subscribe] unless options[:resources_subscribe].nil?
155
- end
156
-
157
176
  def should_include_all?(type)
158
- return true unless @profiles[@active_profile]
177
+ return false unless @profiles[active_profile]
159
178
 
160
- items = @profiles[@active_profile][type]
161
- items.nil? || items.include?("all")
179
+ items = @profiles[active_profile][type]
180
+ # Return true ONLY if items contains "all"
181
+ items && items.include?("all")
162
182
  end
163
183
 
164
184
  def has_rails_version
@@ -173,6 +193,11 @@ module ActionMCP
173
193
  class << self
174
194
  attr_accessor :server, :logger
175
195
 
196
+ # Thread-local storage for active profiles
197
+ def thread_profiles
198
+ @thread_profiles ||= Concurrent::ThreadLocalVar.new(nil)
199
+ end
200
+
176
201
  # Returns the configuration instance.
177
202
  def configuration
178
203
  @configuration ||= Configuration.new
@@ -183,14 +208,19 @@ module ActionMCP
183
208
  yield(configuration)
184
209
  end
185
210
 
186
- # Temporarily use a different profile
187
211
  def with_profile(profile_name)
188
- previous_profile = configuration.active_profile
189
- configuration.use_profile(profile_name)
212
+ previous_profile = thread_profiles.value
213
+ thread_profiles.value = profile_name
214
+
215
+ # Apply the profile options when switching profiles
216
+ configuration.apply_profile_options if configuration
190
217
 
191
218
  yield if block_given?
192
219
  ensure
193
- configuration.use_profile(previous_profile) if block_given?
220
+ thread_profiles.value = previous_profile if block_given?
221
+
222
+ # Reapply the previous profile's options when switching back
223
+ configuration.apply_profile_options if block_given? && configuration
194
224
  end
195
225
  end
196
226
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "gem_version"
4
4
  module ActionMCP
5
- VERSION = "0.27.0"
5
+ VERSION = "0.28.0"
6
6
 
7
7
  class << self
8
8
  alias version gem_version
@@ -1,11 +1,21 @@
1
- primary:
2
- tools:
3
- - all
4
- prompts:
5
- - all
6
- resources:
7
- - all
8
- options:
9
- list_changed: false
10
- resources_subscribe: false
1
+ shared:
2
+ primary:
3
+ tools:
4
+ - all
5
+ prompts:
6
+ - all
7
+ resources:
8
+ - all
9
+ options:
10
+ list_changed: false
11
+ resources_subscribe: false
11
12
 
13
+ # minimal:
14
+ # tools: []
15
+ # prompts: []
16
+ # resources: []
17
+ # options:
18
+ # list_changed: false
19
+ # logging_enabled: false
20
+ # logging_level: :warn
21
+ # resources_subscribe: false
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.27.0
4
+ version: 0.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih