actionmcp 0.71.1 → 0.80.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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +187 -16
  3. data/app/controllers/action_mcp/application_controller.rb +64 -49
  4. data/app/models/action_mcp/session/message.rb +31 -20
  5. data/app/models/action_mcp/session/resource.rb +35 -20
  6. data/app/models/action_mcp/session/sse_event.rb +23 -17
  7. data/app/models/action_mcp/session/subscription.rb +22 -15
  8. data/app/models/action_mcp/session.rb +71 -113
  9. data/config/routes.rb +0 -11
  10. data/db/migrate/20250512154359_consolidated_migration.rb +3 -3
  11. data/db/migrate/20250715070713_add_consents_to_action_mcp_sess.rb +7 -0
  12. data/db/migrate/20250727000001_remove_oauth_support.rb +59 -0
  13. data/lib/action_mcp/base_response.rb +1 -1
  14. data/lib/action_mcp/client/base.rb +9 -11
  15. data/lib/action_mcp/client/elicitation.rb +4 -4
  16. data/lib/action_mcp/client/json_rpc_handler.rb +11 -13
  17. data/lib/action_mcp/client/streamable_http_transport.rb +19 -74
  18. data/lib/action_mcp/client.rb +6 -26
  19. data/lib/action_mcp/configuration.rb +65 -63
  20. data/lib/action_mcp/engine.rb +1 -10
  21. data/lib/action_mcp/filtered_logger.rb +3 -7
  22. data/lib/action_mcp/gateway.rb +7 -11
  23. data/lib/action_mcp/gateway_identifier.rb +187 -3
  24. data/lib/action_mcp/gateway_identifiers/api_key_identifier.rb +56 -0
  25. data/lib/action_mcp/gateway_identifiers/devise_identifier.rb +34 -0
  26. data/lib/action_mcp/gateway_identifiers/request_env_identifier.rb +58 -0
  27. data/lib/action_mcp/gateway_identifiers/warden_identifier.rb +38 -0
  28. data/lib/action_mcp/gateway_identifiers.rb +26 -0
  29. data/lib/action_mcp/json_rpc_handler_base.rb +0 -2
  30. data/lib/action_mcp/prompt.rb +2 -0
  31. data/lib/action_mcp/renderable.rb +1 -1
  32. data/lib/action_mcp/resource_template.rb +6 -2
  33. data/lib/action_mcp/server/{memory_session.rb → base_session.rb} +41 -26
  34. data/lib/action_mcp/server/base_session_store.rb +86 -0
  35. data/lib/action_mcp/server/capabilities.rb +2 -1
  36. data/lib/action_mcp/server/elicitation.rb +3 -9
  37. data/lib/action_mcp/server/error_handling.rb +14 -1
  38. data/lib/action_mcp/server/handlers/router.rb +31 -0
  39. data/lib/action_mcp/server/json_rpc_handler.rb +2 -5
  40. data/lib/action_mcp/server/{messaging.rb → messaging_service.rb} +38 -14
  41. data/lib/action_mcp/server/prompts.rb +4 -4
  42. data/lib/action_mcp/server/resources.rb +23 -4
  43. data/lib/action_mcp/server/session_store_factory.rb +1 -1
  44. data/lib/action_mcp/server/solid_mcp_adapter.rb +9 -10
  45. data/lib/action_mcp/server/tools.rb +62 -43
  46. data/lib/action_mcp/server/transport_handler.rb +2 -4
  47. data/lib/action_mcp/server/volatile_session_store.rb +1 -93
  48. data/lib/action_mcp/tagged_stream_logging.rb +2 -2
  49. data/lib/action_mcp/test_helper/progress_notification_assertions.rb +4 -4
  50. data/lib/action_mcp/test_helper/session_store_assertions.rb +5 -1
  51. data/lib/action_mcp/tool.rb +48 -37
  52. data/lib/action_mcp/types/float_array_type.rb +5 -3
  53. data/lib/action_mcp/version.rb +1 -1
  54. data/lib/action_mcp.rb +2 -7
  55. data/lib/generators/action_mcp/identifier/identifier_generator.rb +189 -0
  56. data/lib/generators/action_mcp/identifier/templates/identifier.rb.erb +35 -0
  57. data/lib/generators/action_mcp/install/install_generator.rb +1 -1
  58. data/lib/generators/action_mcp/install/templates/application_gateway.rb +86 -36
  59. data/lib/generators/action_mcp/install/templates/mcp.yml +4 -21
  60. data/lib/tasks/action_mcp_tasks.rake +7 -5
  61. metadata +18 -100
  62. data/app/controllers/action_mcp/oauth/endpoints_controller.rb +0 -264
  63. data/app/controllers/action_mcp/oauth/metadata_controller.rb +0 -129
  64. data/app/controllers/action_mcp/oauth/registration_controller.rb +0 -206
  65. data/app/models/action_mcp/oauth_client.rb +0 -157
  66. data/app/models/action_mcp/oauth_token.rb +0 -141
  67. data/db/migrate/20250608112101_add_oauth_to_sessions.rb +0 -19
  68. data/db/migrate/20250708105124_create_action_mcp_oauth_clients.rb +0 -42
  69. data/db/migrate/20250708105226_create_action_mcp_oauth_tokens.rb +0 -37
  70. data/lib/action_mcp/client/jwt_client_provider.rb +0 -134
  71. data/lib/action_mcp/client/oauth_client_provider/memory_storage.rb +0 -47
  72. data/lib/action_mcp/client/oauth_client_provider.rb +0 -234
  73. data/lib/action_mcp/jwt_decoder.rb +0 -26
  74. data/lib/action_mcp/jwt_identifier.rb +0 -28
  75. data/lib/action_mcp/none_identifier.rb +0 -19
  76. data/lib/action_mcp/o_auth_identifier.rb +0 -34
  77. data/lib/action_mcp/oauth/active_record_storage.rb +0 -183
  78. data/lib/action_mcp/oauth/error.rb +0 -79
  79. data/lib/action_mcp/oauth/memory_storage.rb +0 -134
  80. data/lib/action_mcp/oauth/middleware.rb +0 -133
  81. data/lib/action_mcp/oauth/provider.rb +0 -426
  82. data/lib/action_mcp/oauth.rb +0 -12
  83. data/lib/action_mcp/omniauth/mcp_strategy.rb +0 -176
  84. data/lib/action_mcp/server/notifications.rb +0 -58
@@ -15,11 +15,9 @@ module ActionMCP
15
15
 
16
16
  attr_reader :session_id, :last_event_id, :protocol_version
17
17
 
18
- def initialize(url, session_store:, session_id: nil, oauth_provider: nil, jwt_provider: nil, protocol_version: nil, **options)
18
+ def initialize(url, session_store:, session_id: nil, protocol_version: nil, **options)
19
19
  super(url, session_store: session_store, **options)
20
20
  @session_id = session_id
21
- @oauth_provider = oauth_provider
22
- @jwt_provider = jwt_provider
23
21
  @protocol_version = protocol_version || ActionMCP::DEFAULT_PROTOCOL_VERSION
24
22
  @negotiated_protocol_version = nil
25
23
  @last_event_id = nil
@@ -66,9 +64,7 @@ module ActionMCP
66
64
  end
67
65
 
68
66
  def send_message(message)
69
- unless ready?
70
- raise ConnectionError, "Transport not ready"
71
- end
67
+ raise ConnectionError, "Transport not ready" unless ready?
72
68
 
73
69
  headers = build_post_headers
74
70
  json_data = message.is_a?(String) ? message : message.to_json
@@ -104,12 +100,8 @@ module ActionMCP
104
100
  headers["Last-Event-ID"] = @last_event_id if @last_event_id
105
101
 
106
102
  # Add MCP-Protocol-Version header for GET requests when we have a negotiated version
107
- if @negotiated_protocol_version
108
- headers["MCP-Protocol-Version"] = @negotiated_protocol_version
109
- end
103
+ headers["MCP-Protocol-Version"] = @negotiated_protocol_version if @negotiated_protocol_version
110
104
 
111
- headers.merge!(oauth_headers)
112
- headers.merge!(jwt_headers)
113
105
  log_debug("Final GET headers: #{headers}")
114
106
  headers
115
107
  end
@@ -123,12 +115,8 @@ module ActionMCP
123
115
 
124
116
  # Add MCP-Protocol-Version header as per 2025-06-18 spec
125
117
  # Only include when we have a negotiated version from previous handshake
126
- if @negotiated_protocol_version
127
- headers["MCP-Protocol-Version"] = @negotiated_protocol_version
128
- end
118
+ headers["MCP-Protocol-Version"] = @negotiated_protocol_version if @negotiated_protocol_version
129
119
 
130
- headers.merge!(oauth_headers)
131
- headers.merge!(jwt_headers)
132
120
  log_debug("Final POST headers: #{headers}")
133
121
  headers
134
122
  end
@@ -154,6 +142,7 @@ module ActionMCP
154
142
  @http_client.get(@url, nil, headers) do |req|
155
143
  req.options.on_data = proc do |chunk, _bytes|
156
144
  break if @stop_requested
145
+
157
146
  process_sse_chunk(chunk)
158
147
  end
159
148
  end
@@ -182,9 +171,9 @@ module ActionMCP
182
171
 
183
172
  lines.each do |line|
184
173
  if line.start_with?("id:")
185
- event_id = line[3..-1].strip
174
+ event_id = line[3..].strip
186
175
  elsif line.start_with?("data:")
187
- data_lines << line[5..-1].strip
176
+ data_lines << line[5..].strip
188
177
  end
189
178
  end
190
179
 
@@ -201,11 +190,9 @@ module ActionMCP
201
190
  end
202
191
  end
203
192
 
204
- def handle_post_response(response, original_message)
193
+ def handle_post_response(response, _original_message)
205
194
  # Extract session ID from response headers
206
- if response.headers["mcp-session-id"]
207
- @session_id = response.headers["mcp-session-id"]
208
- end
195
+ @session_id = response.headers["mcp-session-id"] if response.headers["mcp-session-id"]
209
196
 
210
197
  case response.status
211
198
  when 200
@@ -214,7 +201,6 @@ module ActionMCP
214
201
  # Accepted - message received, no immediate response
215
202
  log_debug("Message accepted (202)")
216
203
  when 401
217
- handle_authentication_error(response)
218
204
  raise AuthenticationError, "Authentication required"
219
205
  when 405
220
206
  # Method not allowed - server doesn't support this operation
@@ -237,19 +223,17 @@ module ActionMCP
237
223
  end
238
224
 
239
225
  def handle_json_response(response)
240
- begin
241
- message = MultiJson.load(response.body)
242
-
243
- # Check if this is an initialize response to capture negotiated protocol version
244
- if message.is_a?(Hash) && message["result"] && message["result"]["protocolVersion"]
245
- @negotiated_protocol_version = message["result"]["protocolVersion"]
246
- log_debug("Negotiated protocol version: #{@negotiated_protocol_version}")
247
- end
226
+ message = MultiJson.load(response.body)
248
227
 
249
- handle_message(message)
250
- rescue MultiJson::ParseError => e
251
- log_error("Failed to parse JSON response: #{e}")
228
+ # Check if this is an initialize response to capture negotiated protocol version
229
+ if message.is_a?(Hash) && message["result"] && message["result"]["protocolVersion"]
230
+ @negotiated_protocol_version = message["result"]["protocolVersion"]
231
+ log_debug("Negotiated protocol version: #{@negotiated_protocol_version}")
252
232
  end
233
+
234
+ handle_message(message)
235
+ rescue MultiJson::ParseError => e
236
+ log_error("Failed to parse JSON response: #{e}")
253
237
  end
254
238
 
255
239
  def handle_sse_response_stream(response)
@@ -261,9 +245,7 @@ module ActionMCP
261
245
 
262
246
  def handle_error_response(response)
263
247
  error_msg = +"HTTP #{response.status}: #{response.reason_phrase}"
264
- if response.body && !response.body.empty?
265
- error_msg << " - #{response.body}"
266
- end
248
+ error_msg << " - #{response.body}" if response.body && !response.body.empty?
267
249
  raise ConnectionError, error_msg
268
250
  end
269
251
 
@@ -315,43 +297,6 @@ module ActionMCP
315
297
  log_debug("Saved session state")
316
298
  end
317
299
 
318
- def oauth_headers
319
- return {} unless @oauth_provider&.authenticated?
320
-
321
- headers = @oauth_provider.authorization_headers
322
- log_debug("OAuth headers: #{headers}") unless headers.empty?
323
- headers
324
- rescue StandardError => e
325
- log_error("Failed to get OAuth headers: #{e.message}")
326
- {}
327
- end
328
-
329
- def jwt_headers
330
- return {} unless @jwt_provider&.authenticated?
331
-
332
- headers = @jwt_provider.authorization_headers
333
- log_debug("JWT headers: #{headers}") unless headers.empty?
334
- headers
335
- rescue StandardError => e
336
- log_error("Failed to get JWT headers: #{e.message}")
337
- {}
338
- end
339
-
340
- def handle_authentication_error(response)
341
- # Check for OAuth challenge in WWW-Authenticate header
342
- www_auth = response.headers["www-authenticate"]
343
- if www_auth&.include?("Bearer")
344
- if @oauth_provider
345
- log_debug("Received OAuth challenge, clearing OAuth tokens")
346
- @oauth_provider.clear_tokens!
347
- end
348
-
349
- if @jwt_provider
350
- log_debug("Received Bearer challenge, clearing JWT tokens")
351
- @jwt_provider.clear_tokens!
352
- end
353
- end
354
- end
355
300
 
356
301
  def user_agent
357
302
  "ActionMCP-StreamableHTTP/#{ActionMCP.gem_version}"
@@ -3,8 +3,6 @@
3
3
  require_relative "client/transport"
4
4
  require_relative "client/session_store"
5
5
  require_relative "client/streamable_http_transport"
6
- require_relative "client/oauth_client_provider"
7
- require_relative "client/jwt_client_provider"
8
6
 
9
7
  module ActionMCP
10
8
  # Creates a client appropriate for the given endpoint.
@@ -13,8 +11,6 @@ module ActionMCP
13
11
  # @param transport [Symbol] The transport type to use (:streamable_http, :sse for legacy)
14
12
  # @param session_store [Symbol] The session store type (:memory, :active_record)
15
13
  # @param session_id [String] Optional session ID for resuming connections
16
- # @param oauth_provider [ActionMCP::Client::OauthClientProvider] Optional OAuth provider for authentication
17
- # @param jwt_provider [ActionMCP::Client::JwtClientProvider] Optional JWT provider for authentication
18
14
  # @param protocol_version [String] The MCP protocol version to use (defaults to ActionMCP::DEFAULT_PROTOCOL_VERSION)
19
15
  # @param logger [Logger] The logger to use. Default is Logger.new($stdout).
20
16
  # @param options [Hash] Additional options to pass to the client constructor.
@@ -39,26 +35,8 @@ module ActionMCP
39
35
  # session_store: :memory
40
36
  # )
41
37
  #
42
- # @example With OAuth authentication
43
- # oauth_provider = ActionMCP::Client::OauthClientProvider.new(
44
- # authorization_server_url: "https://oauth.example.com",
45
- # redirect_url: "http://localhost:3000/callback",
46
- # client_metadata: { client_name: "My App" }
47
- # )
48
- # client = ActionMCP.create_client(
49
- # "http://127.0.0.1:3001/action_mcp",
50
- # oauth_provider: oauth_provider
51
- # )
52
- #
53
- # @example With JWT authentication
54
- # jwt_provider = ActionMCP::Client::JwtClientProvider.new(
55
- # token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
56
- # )
57
- # client = ActionMCP.create_client(
58
- # "http://127.0.0.1:3001/action_mcp",
59
- # jwt_provider: jwt_provider
60
- # )
61
- def self.create_client(endpoint, transport: :streamable_http, session_store: nil, session_id: nil, oauth_provider: nil, jwt_provider: nil, protocol_version: nil, logger: Logger.new($stdout), **options)
38
+ def self.create_client(endpoint, transport: :streamable_http, session_store: nil, session_id: nil,
39
+ protocol_version: nil, logger: Logger.new($stdout), **options)
62
40
  unless endpoint =~ %r{\Ahttps?://}
63
41
  raise ArgumentError, "Only HTTP(S) endpoints are supported. STDIO and other transports are not supported."
64
42
  end
@@ -67,11 +45,13 @@ module ActionMCP
67
45
  store = Client::SessionStoreFactory.create(session_store, **options)
68
46
 
69
47
  # Create transport
70
- transport_instance = create_transport(transport, endpoint, session_store: store, session_id: session_id, oauth_provider: oauth_provider, jwt_provider: jwt_provider, protocol_version: protocol_version, logger: logger, **options)
48
+ transport_instance = create_transport(transport, endpoint, session_store: store, session_id: session_id,
49
+ protocol_version: protocol_version, logger: logger, **options)
71
50
 
72
51
  logger.info("Creating #{transport} client for endpoint: #{endpoint}")
73
52
  # Pass session_id and protocol_version to the client
74
- Client::Base.new(transport: transport_instance, logger: logger, session_id: session_id, protocol_version: protocol_version, **options)
53
+ Client::Base.new(transport: transport_instance, logger: logger, session_id: session_id,
54
+ protocol_version: protocol_version, **options)
75
55
  end
76
56
 
77
57
  private_class_method def self.create_transport(type, endpoint, **options)
@@ -29,7 +29,6 @@ module ActionMCP
29
29
  :verbose_logging,
30
30
  # --- Authentication Options ---
31
31
  :authentication_methods,
32
- :oauth_config,
33
32
  # --- Transport Options ---
34
33
  :sse_heartbeat_interval,
35
34
  :post_response_preference, # :json or :sse
@@ -53,7 +52,7 @@ module ActionMCP
53
52
 
54
53
  def initialize
55
54
  @logging_enabled = true
56
- @list_changed = false
55
+ @list_changed = true
57
56
  @logging_level = :info
58
57
  @resources_subscribe = false
59
58
  @elicitation_enabled = false
@@ -61,13 +60,12 @@ module ActionMCP
61
60
  @active_profile = :primary
62
61
  @profiles = default_profiles
63
62
 
64
- # Authentication defaults
65
- @authentication_methods = Rails.env.production? ? [ "jwt" ] : [ "none" ]
66
- @oauth_config = HashWithIndifferentAccess.new
63
+ # Authentication defaults - empty means all configured identifiers will be tried
64
+ @authentication_methods = []
67
65
 
68
66
  @sse_heartbeat_interval = 30
69
67
  @post_response_preference = :json
70
- @protocol_version = "2025-03-26" # Default to legacy for backwards compatibility
68
+ @protocol_version = "2025-03-26" # Default to legacy for backwards compatibility
71
69
 
72
70
  # Resumability defaults
73
71
  @sse_event_retention_period = 15.minutes
@@ -93,8 +91,8 @@ module ActionMCP
93
91
 
94
92
  def gateway_class
95
93
  if @gateway_class_name
96
- klass = @gateway_class_name.constantize
97
- klass
94
+ @gateway_class_name.constantize
95
+
98
96
  else
99
97
  @gateway_class
100
98
  end
@@ -110,6 +108,9 @@ module ActionMCP
110
108
  # First load defaults from the gem
111
109
  @profiles = default_profiles
112
110
 
111
+ # Preserve any settings that were already set via Rails config
112
+ preserved_name = @name
113
+
113
114
  # Try to load from config/mcp.yml in the Rails app using Rails.config_for
114
115
  begin
115
116
  app_config = Rails.application.config_for(:mcp)
@@ -117,21 +118,27 @@ module ActionMCP
117
118
  raise "Invalid MCP config file" unless app_config.is_a?(Hash)
118
119
 
119
120
  # Extract authentication configuration if present
120
- if app_config["authentication"]
121
- @authentication_methods = Array(app_config["authentication"])
122
- end
123
-
124
- # Extract OAuth configuration if present
125
- if app_config["oauth"]
126
- @oauth_config = HashWithIndifferentAccess.new(app_config["oauth"])
127
- end
121
+ # Handle both symbol and string keys
122
+ @authentication_methods = Array(app_config[:authentication] || app_config["authentication"]) if app_config[:authentication] || app_config["authentication"]
128
123
 
129
124
  # Extract other top-level configuration settings
130
125
  extract_top_level_settings(app_config)
131
126
 
132
- # Extract profiles configuration
133
- if app_config["profiles"]
134
- @profiles = app_config["profiles"]
127
+ # Extract profiles configuration - merge with defaults instead of replacing
128
+ # Rails.config_for returns OrderedOptions which uses symbol keys
129
+ if app_config[:profiles] || app_config["profiles"]
130
+ # Get profiles with either symbol or string key
131
+ app_profiles = app_config[:profiles] || app_config["profiles"]
132
+
133
+ # Convert to regular hash and deep symbolize keys
134
+ if app_profiles.is_a?(ActiveSupport::OrderedOptions)
135
+ app_profiles = app_profiles.to_h.deep_symbolize_keys
136
+ elsif app_profiles.respond_to?(:deep_symbolize_keys)
137
+ app_profiles = app_profiles.deep_symbolize_keys
138
+ end
139
+
140
+ Rails.logger.debug "[Configuration] Merging profiles: #{app_profiles.inspect}"
141
+ @profiles = @profiles.deep_merge(app_profiles)
135
142
  end
136
143
  rescue StandardError => e
137
144
  # If the config file doesn't exist in the Rails app, just use the defaults
@@ -140,8 +147,13 @@ module ActionMCP
140
147
  end
141
148
 
142
149
  # Apply the active profile
150
+ Rails.logger.info "[ActionMCP] Loaded profiles: #{@profiles.keys.join(', ')}"
151
+ Rails.logger.info "[ActionMCP] Using profile: #{@active_profile}"
143
152
  use_profile(@active_profile)
144
153
 
154
+ # Restore preserved settings
155
+ @name = preserved_name if preserved_name
156
+
145
157
  self
146
158
  end
147
159
 
@@ -190,22 +202,21 @@ module ActionMCP
190
202
  capabilities = {}
191
203
  profile = @profiles[active_profile]
192
204
 
205
+ Rails.logger.debug "[ActionMCP] Generating capabilities for profile: #{active_profile}"
206
+ Rails.logger.debug "[ActionMCP] Profile config: #{profile.inspect}"
207
+
193
208
  # Check profile configuration instead of registry contents
194
209
  # If profile includes tools (either "all" or specific tools), advertise tools capability
195
- if profile && profile[:tools] && profile[:tools].any?
196
- capabilities[:tools] = { listChanged: @list_changed }
197
- end
210
+ capabilities[:tools] = { listChanged: @list_changed } if profile && profile[:tools]&.any?
198
211
 
199
212
  # If profile includes prompts, advertise prompts capability
200
- if profile && profile[:prompts] && profile[:prompts].any?
201
- capabilities[:prompts] = { listChanged: @list_changed }
202
- end
213
+ capabilities[:prompts] = { listChanged: @list_changed } if profile && profile[:prompts]&.any?
203
214
 
204
215
  capabilities[:logging] = {} if @logging_enabled
205
216
 
206
217
  # If profile includes resources, advertise resources capability
207
- if profile && profile[:resources] && profile[:resources].any?
208
- capabilities[:resources] = { subscribe: @resources_subscribe }
218
+ if profile && profile[:resources]&.any?
219
+ capabilities[:resources] = { subscribe: @resources_subscribe, listChanged: @list_changed }
209
220
  end
210
221
 
211
222
  capabilities[:elicitation] = {} if @elicitation_enabled
@@ -240,12 +251,12 @@ module ActionMCP
240
251
 
241
252
  # Check if any component type includes "all"
242
253
  needs_eager_load = profile[:tools]&.include?("all") ||
243
- profile[:prompts]&.include?("all") ||
244
- profile[:resources]&.include?("all")
254
+ profile[:prompts]&.include?("all") ||
255
+ profile[:resources]&.include?("all")
245
256
 
246
- if needs_eager_load
247
- ensure_mcp_components_loaded
248
- end
257
+ return unless needs_eager_load
258
+
259
+ ensure_mcp_components_loaded
249
260
  end
250
261
 
251
262
  private
@@ -278,58 +289,48 @@ module ActionMCP
278
289
  end
279
290
 
280
291
  def extract_top_level_settings(app_config)
292
+ # Create a wrapper that handles both symbol and string keys
293
+ config = HashWithIndifferentAccess.new(app_config)
294
+
281
295
  # Extract adapter configuration
282
- if app_config["adapter"]
296
+ if config["adapter"]
283
297
  # This will be handled by the pub/sub system, we just store it for now
284
- @adapter = app_config["adapter"]
298
+ @adapter = config["adapter"]
285
299
  end
286
300
 
287
301
  # Extract thread pool settings
288
- if app_config["min_threads"]
289
- @min_threads = app_config["min_threads"]
290
- end
302
+ @min_threads = config["min_threads"] if config["min_threads"]
291
303
 
292
- if app_config["max_threads"]
293
- @max_threads = app_config["max_threads"]
294
- end
304
+ @max_threads = config["max_threads"] if config["max_threads"]
295
305
 
296
- if app_config["max_queue"]
297
- @max_queue = app_config["max_queue"]
298
- end
306
+ @max_queue = config["max_queue"] if config["max_queue"]
299
307
 
300
308
  # Extract polling interval for solid_cable
301
- if app_config["polling_interval"]
302
- @polling_interval = app_config["polling_interval"]
303
- end
309
+ @polling_interval = config["polling_interval"] if config["polling_interval"]
304
310
 
305
311
  # Extract connects_to setting
306
- if app_config["connects_to"]
307
- @connects_to = app_config["connects_to"]
308
- end
312
+ @connects_to = config["connects_to"] if config["connects_to"]
309
313
 
310
314
  # Extract verbose logging setting
311
- if app_config.key?("verbose_logging")
312
- @verbose_logging = app_config["verbose_logging"]
313
- end
315
+ @verbose_logging = config["verbose_logging"] if app_config.key?("verbose_logging")
314
316
 
315
317
  # Extract gateway class configuration
316
- if app_config["gateway_class"]
317
- @gateway_class_name = app_config["gateway_class"]
318
- end
318
+ @gateway_class_name = config["gateway_class"] if config["gateway_class"]
319
+
320
+ # Extract active profile setting
321
+ @active_profile = config["profile"].to_sym if config["profile"]
319
322
 
320
323
  # Extract session store configuration
321
- if app_config["session_store_type"]
322
- @session_store_type = app_config["session_store_type"].to_sym
323
- end
324
+ @session_store_type = config["session_store_type"].to_sym if config["session_store_type"]
324
325
 
325
326
  # Extract client and server session store types
326
- if app_config["client_session_store_type"]
327
- @client_session_store_type = app_config["client_session_store_type"].to_sym
327
+ if config["client_session_store_type"]
328
+ @client_session_store_type = config["client_session_store_type"].to_sym
328
329
  end
329
330
 
330
- if app_config["server_session_store_type"]
331
- @server_session_store_type = app_config["server_session_store_type"].to_sym
332
- end
331
+ return unless config["server_session_store_type"]
332
+
333
+ @server_session_store_type = config["server_session_store_type"].to_sym
333
334
  end
334
335
 
335
336
  def should_include_all?(type)
@@ -377,6 +378,7 @@ module ActionMCP
377
378
  Dir.glob(mcp_path.join("**/*.rb")).sort.each do |file|
378
379
  # Skip base classes we already loaded
379
380
  next if base_files.any? { |base| file == base.to_s }
381
+
380
382
  require_dependency file
381
383
  end
382
384
  end
@@ -12,7 +12,6 @@ module ActionMCP
12
12
  ActiveSupport::Inflector.inflections(:en) do |inflect|
13
13
  inflect.acronym "SSE"
14
14
  inflect.acronym "MCP"
15
- inflect.acronym "OAuth"
16
15
  end
17
16
 
18
17
  # Provide a configuration namespace for ActionMCP
@@ -54,12 +53,6 @@ module ActionMCP
54
53
  ActionMCP.configuration.load_profiles
55
54
  end
56
55
 
57
- # Add OAuth middleware if OAuth is configured
58
- initializer "action_mcp.oauth_middleware", after: "action_mcp.load_profiles" do
59
- if ActionMCP.configuration.authentication_methods&.include?("oauth")
60
- config.middleware.use ActionMCP::OAuth::Middleware
61
- end
62
- end
63
56
 
64
57
  # Configure autoloading for the mcp/tools directory and identifiers
65
58
  initializer "action_mcp.autoloading", before: :set_autoload_paths do |app|
@@ -77,9 +70,7 @@ module ActionMCP
77
70
  end
78
71
 
79
72
  # Add identifiers directory for gateway identifiers
80
- if identifiers_path.exist?
81
- app.autoloaders.main.push_dir(identifiers_path, namespace: Object)
82
- end
73
+ app.autoloaders.main.push_dir(identifiers_path, namespace: Object) if identifiers_path.exist?
83
74
  end
84
75
 
85
76
  # Initialize the ActionMCP logger.
@@ -3,11 +3,7 @@
3
3
  module ActionMCP
4
4
  # Custom logger that filters out repetitive MCP requests
5
5
  class FilteredLogger < ActiveSupport::Logger
6
- FILTERED_PATHS = [
7
- "/oauth/authorize",
8
- "/.well-known/oauth-protected-resource",
9
- "/.well-known/oauth-authorization-server"
10
- ].freeze
6
+ FILTERED_PATHS = [].freeze
11
7
 
12
8
  FILTERED_METHODS = [
13
9
  "notifications/initialized",
@@ -15,8 +11,8 @@ module ActionMCP
15
11
  ].freeze
16
12
 
17
13
  def add(severity, message = nil, progname = nil, &block)
18
- # Filter out repetitive OAuth metadata requests
19
- if message && message.is_a?(String)
14
+ # Filter out specific paths
15
+ if message.is_a?(String)
20
16
  return if FILTERED_PATHS.any? { |path| message.include?(path) && message.include?("200 OK") }
21
17
 
22
18
  # Filter out repetitive MCP notifications
@@ -31,21 +31,17 @@ module ActionMCP
31
31
  def authenticate!
32
32
  active_identifiers = filter_active_identifiers
33
33
 
34
- if active_identifiers.empty?
35
- raise ActionMCP::UnauthorizedError, "No authentication methods available"
36
- end
34
+ raise ActionMCP::UnauthorizedError, "No authentication methods available" if active_identifiers.empty?
37
35
 
38
36
  # Try identifiers in order, use the first one that succeeds
39
37
  last_error = nil
40
38
  active_identifiers.each do |klass|
41
- begin
42
- result = klass.new(@request).resolve
43
- return { klass.identifier_name => result }
44
- rescue ActionMCP::GatewayIdentifier::Unauthorized => e
45
- last_error = e
46
- # Try next identifier
47
- next
48
- end
39
+ result = klass.new(@request).resolve
40
+ return { klass.identifier_name => result }
41
+ rescue ActionMCP::GatewayIdentifier::Unauthorized => e
42
+ last_error = e
43
+ # Try next identifier
44
+ next
49
45
  end
50
46
 
51
47
  # If we get here, all identifiers failed