actionmcp 0.70.0 → 0.71.1
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/README.md +46 -41
- data/app/controllers/action_mcp/application_controller.rb +67 -15
- data/app/controllers/action_mcp/oauth/metadata_controller.rb +13 -13
- data/app/controllers/action_mcp/oauth/registration_controller.rb +206 -0
- data/app/models/action_mcp/oauth_client.rb +157 -0
- data/app/models/action_mcp/oauth_token.rb +141 -0
- data/app/models/action_mcp/session/message.rb +12 -12
- data/app/models/action_mcp/session/resource.rb +2 -2
- data/app/models/action_mcp/session/sse_event.rb +2 -2
- data/app/models/action_mcp/session/subscription.rb +2 -2
- data/app/models/action_mcp/session.rb +22 -22
- data/config/routes.rb +1 -0
- data/db/migrate/20250708105124_create_action_mcp_oauth_clients.rb +42 -0
- data/db/migrate/20250708105226_create_action_mcp_oauth_tokens.rb +37 -0
- data/lib/action_mcp/client/base.rb +3 -2
- data/lib/action_mcp/client/collection.rb +3 -3
- data/lib/action_mcp/client/jwt_client_provider.rb +134 -0
- data/lib/action_mcp/client/streamable_http_transport.rb +56 -10
- data/lib/action_mcp/client.rb +16 -4
- data/lib/action_mcp/configuration.rb +27 -4
- data/lib/action_mcp/engine.rb +7 -1
- data/lib/action_mcp/filtered_logger.rb +32 -0
- data/lib/action_mcp/gateway.rb +47 -133
- data/lib/action_mcp/gateway_identifier.rb +29 -0
- data/lib/action_mcp/jwt_identifier.rb +28 -0
- data/lib/action_mcp/none_identifier.rb +19 -0
- data/lib/action_mcp/o_auth_identifier.rb +34 -0
- data/lib/action_mcp/oauth/active_record_storage.rb +183 -0
- data/lib/action_mcp/oauth/memory_storage.rb +23 -1
- data/lib/action_mcp/oauth/middleware.rb +33 -0
- data/lib/action_mcp/oauth/provider.rb +49 -13
- data/lib/action_mcp/oauth.rb +12 -0
- data/lib/action_mcp/server/capabilities.rb +0 -3
- data/lib/action_mcp/server/resources.rb +1 -1
- data/lib/action_mcp/server/tools.rb +36 -24
- data/lib/action_mcp/sse_listener.rb +0 -7
- data/lib/action_mcp/test_helper.rb +5 -0
- data/lib/action_mcp/tool.rb +94 -4
- data/lib/action_mcp/tools_registry.rb +3 -0
- data/lib/action_mcp/version.rb +1 -1
- data/lib/generators/action_mcp/install/templates/mcp.yml +16 -16
- metadata +14 -1
@@ -79,7 +79,7 @@ module ActionMCP
|
|
79
79
|
|
80
80
|
# Generate refresh token if enabled
|
81
81
|
refresh_token = nil
|
82
|
-
if oauth_config[
|
82
|
+
if oauth_config[:enable_refresh_tokens]
|
83
83
|
refresh_token = generate_refresh_token(
|
84
84
|
client_id: client_id,
|
85
85
|
scope: code_data[:scope],
|
@@ -206,13 +206,29 @@ module ActionMCP
|
|
206
206
|
revoked
|
207
207
|
end
|
208
208
|
|
209
|
+
# Register a new OAuth client (Dynamic Client Registration)
|
210
|
+
# @param client_info [Hash] Client registration information
|
211
|
+
# @return [Hash] Registered client information
|
212
|
+
def register_client(client_info)
|
213
|
+
# Store client registration
|
214
|
+
storage.store_client_registration(client_info[:client_id], client_info)
|
215
|
+
client_info
|
216
|
+
end
|
217
|
+
|
218
|
+
# Retrieve registered client information
|
219
|
+
# @param client_id [String] OAuth client identifier
|
220
|
+
# @return [Hash, nil] Client information or nil if not found
|
221
|
+
def get_client(client_id)
|
222
|
+
storage.retrieve_client_registration(client_id)
|
223
|
+
end
|
224
|
+
|
209
225
|
# Client Credentials Grant (for server-to-server)
|
210
226
|
# @param client_id [String] OAuth client identifier
|
211
227
|
# @param client_secret [String] OAuth client secret
|
212
228
|
# @param scope [String] Requested scope
|
213
229
|
# @return [Hash] Token response
|
214
230
|
def client_credentials_grant(client_id:, client_secret:, scope: nil)
|
215
|
-
unless oauth_config[
|
231
|
+
unless oauth_config[:enable_client_credentials]
|
216
232
|
raise UnsupportedGrantTypeError, "Client credentials grant not supported"
|
217
233
|
end
|
218
234
|
|
@@ -244,19 +260,37 @@ module ActionMCP
|
|
244
260
|
private
|
245
261
|
|
246
262
|
def oauth_config
|
247
|
-
@oauth_config ||= ActionMCP.configuration.oauth_config || {}
|
263
|
+
@oauth_config ||= HashWithIndifferentAccess.new(ActionMCP.configuration.oauth_config || {})
|
248
264
|
end
|
249
265
|
|
250
266
|
def validate_client(client_id, client_secret, require_secret: false)
|
251
|
-
#
|
252
|
-
|
253
|
-
|
267
|
+
# First check if client is registered via dynamic registration
|
268
|
+
client_info = get_client(client_id)
|
269
|
+
if client_info
|
270
|
+
# Validate client secret for confidential clients
|
271
|
+
if client_info[:client_secret]
|
272
|
+
unless client_secret == client_info[:client_secret]
|
273
|
+
raise InvalidClientError, "Invalid client credentials"
|
274
|
+
end
|
275
|
+
elsif require_secret
|
276
|
+
raise InvalidClientError, "Client authentication required"
|
277
|
+
end
|
278
|
+
return true
|
279
|
+
end
|
280
|
+
|
281
|
+
# Fall back to custom provider validation
|
282
|
+
provider_class = oauth_config[:provider]
|
254
283
|
if provider_class && provider_class.respond_to?(:validate_client)
|
255
284
|
provider_class.validate_client(client_id, client_secret)
|
256
285
|
elsif require_secret && client_secret.nil?
|
257
286
|
raise InvalidClientError, "Client authentication required"
|
287
|
+
else
|
288
|
+
# In development, allow unregistered clients if configured
|
289
|
+
if Rails.env.development? && oauth_config[:allow_unregistered_clients] != false
|
290
|
+
return true
|
291
|
+
end
|
292
|
+
raise InvalidClientError, "Unknown client"
|
258
293
|
end
|
259
|
-
# Default: allow any client for development
|
260
294
|
end
|
261
295
|
|
262
296
|
def validate_pkce(code_challenge, method, code_verifier)
|
@@ -271,7 +305,7 @@ module ActionMCP
|
|
271
305
|
raise InvalidGrantError, "Invalid code verifier"
|
272
306
|
end
|
273
307
|
when "plain"
|
274
|
-
unless oauth_config[
|
308
|
+
unless oauth_config[:allow_plain_pkce]
|
275
309
|
raise InvalidGrantError, "Plain PKCE not allowed"
|
276
310
|
end
|
277
311
|
unless code_challenge == code_verifier
|
@@ -283,7 +317,7 @@ module ActionMCP
|
|
283
317
|
end
|
284
318
|
|
285
319
|
def validate_scope(scope)
|
286
|
-
supported_scopes = oauth_config
|
320
|
+
supported_scopes = oauth_config.fetch(:scopes_supported, [ "mcp:tools", "mcp:resources", "mcp:prompts" ])
|
287
321
|
requested_scopes = scope.split(" ")
|
288
322
|
unsupported = requested_scopes - supported_scopes
|
289
323
|
if unsupported.any?
|
@@ -292,7 +326,7 @@ module ActionMCP
|
|
292
326
|
end
|
293
327
|
|
294
328
|
def default_scope
|
295
|
-
oauth_config
|
329
|
+
oauth_config.fetch(:default_scope, "mcp:tools mcp:resources mcp:prompts")
|
296
330
|
end
|
297
331
|
|
298
332
|
def generate_access_token(client_id:, scope:, user_id:)
|
@@ -325,17 +359,19 @@ module ActionMCP
|
|
325
359
|
end
|
326
360
|
|
327
361
|
def token_expires_in
|
328
|
-
oauth_config
|
362
|
+
oauth_config.fetch(:access_token_expires_in, 3600) # 1 hour
|
329
363
|
end
|
330
364
|
|
331
365
|
def refresh_token_expires_in
|
332
|
-
oauth_config
|
366
|
+
oauth_config.fetch(:refresh_token_expires_in, 7.days.to_i) # 1 week
|
333
367
|
end
|
334
368
|
|
335
369
|
# Storage methods - these delegate to a configurable storage backend
|
336
370
|
def storage
|
337
371
|
@storage ||= begin
|
338
|
-
|
372
|
+
# Default to ActiveRecord storage for production, memory for test
|
373
|
+
default_storage = Rails.env.test? ? "ActionMCP::OAuth::MemoryStorage" : "ActionMCP::OAuth::ActiveRecordStorage"
|
374
|
+
storage_class = oauth_config.fetch(:storage, default_storage)
|
339
375
|
storage_class = storage_class.constantize if storage_class.is_a?(String)
|
340
376
|
storage_class.new
|
341
377
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionMCP
|
4
|
+
module OAuth
|
5
|
+
# Load OAuth components
|
6
|
+
autoload :Error, "action_mcp/oauth/error"
|
7
|
+
autoload :Provider, "action_mcp/oauth/provider"
|
8
|
+
autoload :Middleware, "action_mcp/oauth/middleware"
|
9
|
+
autoload :MemoryStorage, "action_mcp/oauth/memory_storage"
|
10
|
+
autoload :ActiveRecordStorage, "action_mcp/oauth/active_record_storage"
|
11
|
+
end
|
12
|
+
end
|
@@ -40,14 +40,11 @@ module ActionMCP
|
|
40
40
|
if existing_session && existing_session.initialized?
|
41
41
|
# Resume existing session - update transport reference
|
42
42
|
transport.instance_variable_set(:@session, existing_session)
|
43
|
-
Rails.logger.info("Resuming existing session: #{session_id}")
|
44
43
|
|
45
44
|
# Return existing session info
|
46
45
|
capabilities_payload = existing_session.server_capabilities_payload
|
47
46
|
capabilities_payload[:protocolVersion] = client_protocol_version
|
48
47
|
return send_jsonrpc_response(request_id, result: capabilities_payload)
|
49
|
-
else
|
50
|
-
Rails.logger.warn("Session #{session_id} not found or not initialized, creating new session")
|
51
48
|
end
|
52
49
|
end
|
53
50
|
|
@@ -75,7 +75,7 @@ module ActionMCP
|
|
75
75
|
# @example Output:
|
76
76
|
# # Logs: "Registered Resource Templates: ["db://{table}", "file://{path}"]"
|
77
77
|
def log_resource_templates
|
78
|
-
|
78
|
+
# Resource templates: #{ActionMCP::ResourceTemplatesRegistry.resource_templates.keys}
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
@@ -41,35 +41,47 @@ module ActionMCP
|
|
41
41
|
tool_class = session.registered_tools.find { |t| t.tool_name == tool_name }
|
42
42
|
|
43
43
|
if tool_class
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
44
|
+
begin
|
45
|
+
# Create tool and set execution context with request info
|
46
|
+
tool = tool_class.new(arguments)
|
47
|
+
tool.with_context({
|
48
|
+
session: session,
|
49
|
+
request: {
|
50
|
+
params: {
|
51
|
+
name: tool_name,
|
52
|
+
arguments: arguments,
|
53
|
+
_meta: _meta
|
54
|
+
}
|
53
55
|
}
|
54
|
-
}
|
55
|
-
})
|
56
|
+
})
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
# Wrap tool execution with Rails reloader for development
|
59
|
+
result = if Rails.env.development?
|
60
|
+
# Preserve Current attributes across reloader boundary
|
61
|
+
current_user = ActionMCP::Current.user
|
62
|
+
current_gateway = ActionMCP::Current.gateway
|
63
|
+
|
64
|
+
Rails.application.reloader.wrap do
|
65
|
+
# Restore Current attributes inside reloader
|
66
|
+
ActionMCP::Current.user = current_user
|
67
|
+
ActionMCP::Current.gateway = current_gateway
|
68
|
+
tool.call
|
69
|
+
end
|
70
|
+
else
|
60
71
|
tool.call
|
61
72
|
end
|
62
|
-
else
|
63
|
-
tool.call
|
64
|
-
end
|
65
73
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
74
|
+
if result.is_error
|
75
|
+
# Convert ToolResponse error to proper JSON-RPC error format
|
76
|
+
# Pass the error hash directly - the Response class will handle it
|
77
|
+
error_hash = result.to_h
|
78
|
+
send_jsonrpc_response(request_id, error: error_hash)
|
79
|
+
else
|
80
|
+
send_jsonrpc_response(request_id, result: result)
|
81
|
+
end
|
82
|
+
rescue ArgumentError => e
|
83
|
+
# Handle parameter validation errors
|
84
|
+
send_jsonrpc_error(request_id, :invalid_params, e.message)
|
73
85
|
end
|
74
86
|
else
|
75
87
|
send_jsonrpc_error(request_id, :method_not_found, "Tool '#{tool_name}' not available in this session")
|
@@ -19,10 +19,7 @@ module ActionMCP
|
|
19
19
|
# @yield [Hash] Yields parsed message received from the pub/sub channel
|
20
20
|
# @return [Boolean] True if subscription was successful within timeout, false otherwise.
|
21
21
|
def start(&callback)
|
22
|
-
Rails.logger.debug "SSEListener: Starting for channel: #{session_key}"
|
23
|
-
|
24
22
|
success_callback = lambda {
|
25
|
-
Rails.logger.info "SSEListener: Successfully subscribed to channel: #{session_key}"
|
26
23
|
@subscription_active.make_true
|
27
24
|
}
|
28
25
|
|
@@ -41,7 +38,6 @@ module ActionMCP
|
|
41
38
|
return if @stopped.true?
|
42
39
|
|
43
40
|
@stopped.make_true
|
44
|
-
Rails.logger.debug "SSEListener: Stopping listener for channel: #{session_key}"
|
45
41
|
end
|
46
42
|
|
47
43
|
private
|
@@ -50,8 +46,6 @@ module ActionMCP
|
|
50
46
|
return if @stopped.true?
|
51
47
|
|
52
48
|
begin
|
53
|
-
Rails.logger.debug "SSEListener: Received raw message of type: #{raw_message.class}"
|
54
|
-
|
55
49
|
# Check if the message is a valid JSON string or has a message attribute
|
56
50
|
if raw_message.is_a?(String) && valid_json_format?(raw_message)
|
57
51
|
message = MultiJson.load(raw_message)
|
@@ -80,7 +74,6 @@ module ActionMCP
|
|
80
74
|
begin
|
81
75
|
subscription_future.value(5) || @subscription_active.true?
|
82
76
|
rescue Concurrent::TimeoutError
|
83
|
-
Rails.logger.warn "SSEListener: Timed out waiting for subscription for #{session_key}"
|
84
77
|
false
|
85
78
|
end
|
86
79
|
end
|
@@ -49,6 +49,11 @@ module ActionMCP
|
|
49
49
|
end
|
50
50
|
alias execute_tool execute_mcp_tool
|
51
51
|
|
52
|
+
def execute_mcp_tool_with_error(name, args = {})
|
53
|
+
ActionMCP::ToolsRegistry.tool_call(name, args)
|
54
|
+
end
|
55
|
+
alias execute_tool_with_error execute_mcp_tool_with_error
|
56
|
+
|
52
57
|
def execute_mcp_prompt(name, args = {})
|
53
58
|
resp = ActionMCP::PromptsRegistry.prompt_call(name, args)
|
54
59
|
assert !resp.is_error, "Prompt #{name.inspect} returned error: #{resp.to_h[:message]}"
|
data/lib/action_mcp/tool.rb
CHANGED
@@ -176,7 +176,7 @@ module ActionMCP
|
|
176
176
|
|
177
177
|
return unless %w[number integer].include?(type)
|
178
178
|
|
179
|
-
validates prop_name, numericality: true, allow_nil:
|
179
|
+
validates prop_name, numericality: true, allow_nil: !required
|
180
180
|
end
|
181
181
|
|
182
182
|
# --------------------------------------------------------------------------
|
@@ -257,6 +257,13 @@ module ActionMCP
|
|
257
257
|
# Instance Methods
|
258
258
|
# --------------------------------------------------------------------------
|
259
259
|
|
260
|
+
# Override initialize to validate parameters before ActiveModel conversion
|
261
|
+
def initialize(attributes = {})
|
262
|
+
# Validate parameters before ActiveModel processes them
|
263
|
+
validate_parameter_types(attributes)
|
264
|
+
super
|
265
|
+
end
|
266
|
+
|
260
267
|
# Public entry point for executing the tool
|
261
268
|
# Returns an array of Content objects collected from render calls
|
262
269
|
def call
|
@@ -273,14 +280,16 @@ module ActionMCP
|
|
273
280
|
@response.mark_as_error!(:internal_error, message: e.message)
|
274
281
|
end
|
275
282
|
else
|
276
|
-
@response.mark_as_error!(:
|
283
|
+
@response.mark_as_error!(:invalid_params,
|
277
284
|
message: "Invalid input",
|
278
285
|
data: errors.full_messages)
|
279
286
|
end
|
280
287
|
|
281
288
|
# If callbacks halted execution (`performed` still false) and
|
282
|
-
# nothing else marked an error, surface it as
|
283
|
-
|
289
|
+
# nothing else marked an error, surface it as invalid_params.
|
290
|
+
if !performed && !@response.error?
|
291
|
+
@response.mark_as_error!(:invalid_params, message: "Tool execution was aborted")
|
292
|
+
end
|
284
293
|
|
285
294
|
@response
|
286
295
|
end
|
@@ -362,5 +371,86 @@ module ActionMCP
|
|
362
371
|
end
|
363
372
|
|
364
373
|
private_class_method :map_json_type_to_active_model_type
|
374
|
+
|
375
|
+
private
|
376
|
+
|
377
|
+
# Validates parameter types before ActiveModel conversion
|
378
|
+
def validate_parameter_types(attributes)
|
379
|
+
return unless attributes.is_a?(Hash)
|
380
|
+
|
381
|
+
attributes.each do |key, value|
|
382
|
+
key_str = key.to_s
|
383
|
+
property_schema = self.class._schema_properties[key_str]
|
384
|
+
|
385
|
+
next unless property_schema
|
386
|
+
|
387
|
+
expected_type = property_schema[:type]
|
388
|
+
|
389
|
+
# Skip validation if value is nil and property is not required
|
390
|
+
next if value.nil? && !self.class._required_properties.include?(key_str)
|
391
|
+
|
392
|
+
# Validate based on expected JSON Schema type
|
393
|
+
case expected_type
|
394
|
+
when "number"
|
395
|
+
validate_number_parameter(key_str, value)
|
396
|
+
when "integer"
|
397
|
+
validate_integer_parameter(key_str, value)
|
398
|
+
when "string"
|
399
|
+
validate_string_parameter(key_str, value)
|
400
|
+
when "boolean"
|
401
|
+
validate_boolean_parameter(key_str, value)
|
402
|
+
when "array"
|
403
|
+
validate_array_parameter(key_str, value, property_schema)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def validate_number_parameter(key, value)
|
409
|
+
return if value.is_a?(Numeric)
|
410
|
+
|
411
|
+
if value.is_a?(String)
|
412
|
+
# Check if string can be converted to a valid number
|
413
|
+
begin
|
414
|
+
Float(value)
|
415
|
+
rescue ArgumentError, TypeError
|
416
|
+
raise ArgumentError, "Parameter '#{key}' must be a valid number, got: #{value.inspect}"
|
417
|
+
end
|
418
|
+
else
|
419
|
+
raise ArgumentError, "Parameter '#{key}' must be a number, got: #{value.class}"
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def validate_integer_parameter(key, value)
|
424
|
+
return if value.is_a?(Integer)
|
425
|
+
|
426
|
+
if value.is_a?(String)
|
427
|
+
# Check if string can be converted to a valid integer
|
428
|
+
begin
|
429
|
+
Integer(value)
|
430
|
+
rescue ArgumentError, TypeError
|
431
|
+
raise ArgumentError, "Parameter '#{key}' must be a valid integer, got: #{value.inspect}"
|
432
|
+
end
|
433
|
+
else
|
434
|
+
raise ArgumentError, "Parameter '#{key}' must be an integer, got: #{value.class}"
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
def validate_string_parameter(key, value)
|
439
|
+
return if value.is_a?(String)
|
440
|
+
|
441
|
+
raise ArgumentError, "Parameter '#{key}' must be a string, got: #{value.class}"
|
442
|
+
end
|
443
|
+
|
444
|
+
def validate_boolean_parameter(key, value)
|
445
|
+
return if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
446
|
+
|
447
|
+
raise ArgumentError, "Parameter '#{key}' must be a boolean, got: #{value.class}"
|
448
|
+
end
|
449
|
+
|
450
|
+
def validate_array_parameter(key, value, property_schema)
|
451
|
+
return if value.is_a?(Array)
|
452
|
+
|
453
|
+
raise ArgumentError, "Parameter '#{key}' must be an array, got: #{value.class}"
|
454
|
+
end
|
365
455
|
end
|
366
456
|
end
|
@@ -22,6 +22,9 @@ module ActionMCP
|
|
22
22
|
tool.call
|
23
23
|
rescue RegistryBase::NotFound
|
24
24
|
error_response(:invalid_params, message: "Tool not found: #{tool_name}")
|
25
|
+
rescue ArgumentError => e
|
26
|
+
# Handle parameter validation errors
|
27
|
+
error_response(:invalid_params, message: e.message)
|
25
28
|
rescue StandardError => e
|
26
29
|
# FIXME, we should maybe not return the error message to the user
|
27
30
|
error_response(:invalid_params, message: "Tool execution failed: #{e.message}")
|
data/lib/action_mcp/version.rb
CHANGED
@@ -5,17 +5,17 @@
|
|
5
5
|
shared:
|
6
6
|
# Authentication configuration - array of methods to try in order
|
7
7
|
authentication: ["none"] # No authentication required by default
|
8
|
-
|
8
|
+
|
9
9
|
# Session store configuration
|
10
10
|
# Global session store type used by both client and server (default: volatile in dev/test, active_record in production)
|
11
11
|
# session_store_type: volatile
|
12
|
-
|
12
|
+
|
13
13
|
# Client-specific session store type (falls back to session_store_type if not specified)
|
14
14
|
# client_session_store_type: volatile
|
15
|
-
|
15
|
+
|
16
16
|
# Server-specific session store type (falls back to session_store_type if not specified)
|
17
17
|
# server_session_store_type: active_record
|
18
|
-
|
18
|
+
|
19
19
|
# OAuth configuration (if using OAuth authentication)
|
20
20
|
# oauth:
|
21
21
|
# provider: "demo_oauth_provider"
|
@@ -24,7 +24,7 @@ shared:
|
|
24
24
|
# enable_token_revocation: true
|
25
25
|
# pkce_required: true
|
26
26
|
# issuer_url: https://yourapp.com
|
27
|
-
|
27
|
+
|
28
28
|
# MCP capability profiles
|
29
29
|
profiles:
|
30
30
|
primary:
|
@@ -38,7 +38,7 @@ shared:
|
|
38
38
|
list_changed: false
|
39
39
|
logging_enabled: true
|
40
40
|
resources_subscribe: false
|
41
|
-
|
41
|
+
|
42
42
|
minimal:
|
43
43
|
tools: []
|
44
44
|
prompts: []
|
@@ -53,13 +53,13 @@ shared:
|
|
53
53
|
development:
|
54
54
|
# Use simple pub/sub adapter for development
|
55
55
|
adapter: simple
|
56
|
-
|
56
|
+
|
57
57
|
# Session store examples for development
|
58
58
|
# Use volatile client sessions for faster development
|
59
59
|
# client_session_store_type: volatile
|
60
60
|
# Use persistent server sessions to maintain state across restarts
|
61
61
|
# server_session_store_type: active_record
|
62
|
-
|
62
|
+
|
63
63
|
# Thread pool configuration (optional)
|
64
64
|
# min_threads: 5 # Minimum number of threads in the pool
|
65
65
|
# max_threads: 10 # Maximum number of threads in the pool
|
@@ -69,10 +69,10 @@ development:
|
|
69
69
|
test:
|
70
70
|
# JWT authentication for testing environment
|
71
71
|
authentication: ["jwt"]
|
72
|
-
|
72
|
+
|
73
73
|
# Test adapter for testing
|
74
74
|
adapter: test
|
75
|
-
|
75
|
+
|
76
76
|
# Use volatile sessions for testing (fast cleanup)
|
77
77
|
# session_store_type: volatile
|
78
78
|
|
@@ -80,7 +80,7 @@ test:
|
|
80
80
|
production:
|
81
81
|
# Multiple authentication methods - try OAuth first, fallback to JWT
|
82
82
|
authentication: ["oauth", "jwt"]
|
83
|
-
|
83
|
+
|
84
84
|
# OAuth configuration for production
|
85
85
|
oauth:
|
86
86
|
provider: "application_oauth_provider" # Your custom provider class
|
@@ -89,29 +89,29 @@ production:
|
|
89
89
|
enable_token_revocation: true
|
90
90
|
pkce_required: true
|
91
91
|
issuer_url: https://yourapp.com
|
92
|
-
|
92
|
+
|
93
93
|
# Additional production profiles for external clients
|
94
94
|
profiles:
|
95
95
|
external_clients:
|
96
96
|
tools: ["WeatherForecastTool"] # Limited tool access for external clients
|
97
97
|
prompts: []
|
98
98
|
resources: []
|
99
|
-
|
99
|
+
|
100
100
|
# Production session store configuration
|
101
101
|
# Use persistent storage for production reliability
|
102
102
|
# session_store_type: active_record
|
103
103
|
# Or configure separately:
|
104
104
|
# client_session_store_type: active_record # Client connections persist across restarts
|
105
105
|
# server_session_store_type: active_record # Server state persists across deployments
|
106
|
-
|
106
|
+
|
107
107
|
# Choose one of the following adapters:
|
108
|
-
|
108
|
+
|
109
109
|
# 1. Database-backed adapter (recommended)
|
110
110
|
adapter: solid_mcp
|
111
111
|
polling_interval: 0.5.seconds
|
112
112
|
batch_size: 200 # Number of messages to write in a single batch
|
113
113
|
flush_interval: 0.05 # Seconds between batch flushes
|
114
|
-
|
114
|
+
|
115
115
|
# Thread pool configuration (optional)
|
116
116
|
min_threads: 10 # Minimum number of threads in the pool
|
117
117
|
max_threads: 20 # Maximum number of threads in the pool
|
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.
|
4
|
+
version: 0.71.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abdelkader Boudih
|
@@ -206,8 +206,11 @@ files:
|
|
206
206
|
- app/controllers/action_mcp/application_controller.rb
|
207
207
|
- app/controllers/action_mcp/oauth/endpoints_controller.rb
|
208
208
|
- app/controllers/action_mcp/oauth/metadata_controller.rb
|
209
|
+
- app/controllers/action_mcp/oauth/registration_controller.rb
|
209
210
|
- app/models/action_mcp.rb
|
210
211
|
- app/models/action_mcp/application_record.rb
|
212
|
+
- app/models/action_mcp/oauth_client.rb
|
213
|
+
- app/models/action_mcp/oauth_token.rb
|
211
214
|
- app/models/action_mcp/session.rb
|
212
215
|
- app/models/action_mcp/session/message.rb
|
213
216
|
- app/models/action_mcp/session/resource.rb
|
@@ -218,6 +221,8 @@ files:
|
|
218
221
|
- config/routes.rb
|
219
222
|
- db/migrate/20250512154359_consolidated_migration.rb
|
220
223
|
- db/migrate/20250608112101_add_oauth_to_sessions.rb
|
224
|
+
- db/migrate/20250708105124_create_action_mcp_oauth_clients.rb
|
225
|
+
- db/migrate/20250708105226_create_action_mcp_oauth_tokens.rb
|
221
226
|
- exe/actionmcp_cli
|
222
227
|
- lib/action_mcp.rb
|
223
228
|
- lib/action_mcp/base_response.rb
|
@@ -231,6 +236,7 @@ files:
|
|
231
236
|
- lib/action_mcp/client/collection.rb
|
232
237
|
- lib/action_mcp/client/elicitation.rb
|
233
238
|
- lib/action_mcp/client/json_rpc_handler.rb
|
239
|
+
- lib/action_mcp/client/jwt_client_provider.rb
|
234
240
|
- lib/action_mcp/client/logging.rb
|
235
241
|
- lib/action_mcp/client/messaging.rb
|
236
242
|
- lib/action_mcp/client/oauth_client_provider.rb
|
@@ -262,7 +268,9 @@ files:
|
|
262
268
|
- lib/action_mcp/current.rb
|
263
269
|
- lib/action_mcp/current_helpers.rb
|
264
270
|
- lib/action_mcp/engine.rb
|
271
|
+
- lib/action_mcp/filtered_logger.rb
|
265
272
|
- lib/action_mcp/gateway.rb
|
273
|
+
- lib/action_mcp/gateway_identifier.rb
|
266
274
|
- lib/action_mcp/gem_version.rb
|
267
275
|
- lib/action_mcp/instrumentation/controller_runtime.rb
|
268
276
|
- lib/action_mcp/instrumentation/instrumentation.rb
|
@@ -270,8 +278,13 @@ files:
|
|
270
278
|
- lib/action_mcp/integer_array.rb
|
271
279
|
- lib/action_mcp/json_rpc_handler_base.rb
|
272
280
|
- lib/action_mcp/jwt_decoder.rb
|
281
|
+
- lib/action_mcp/jwt_identifier.rb
|
273
282
|
- lib/action_mcp/log_subscriber.rb
|
274
283
|
- lib/action_mcp/logging.rb
|
284
|
+
- lib/action_mcp/none_identifier.rb
|
285
|
+
- lib/action_mcp/o_auth_identifier.rb
|
286
|
+
- lib/action_mcp/oauth.rb
|
287
|
+
- lib/action_mcp/oauth/active_record_storage.rb
|
275
288
|
- lib/action_mcp/oauth/error.rb
|
276
289
|
- lib/action_mcp/oauth/memory_storage.rb
|
277
290
|
- lib/action_mcp/oauth/middleware.rb
|