actionmcp 0.60.2 → 0.71.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 +4 -4
- data/README.md +46 -59
- data/app/controllers/action_mcp/application_controller.rb +95 -28
- 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 +68 -43
- 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/capability.rb +2 -0
- data/lib/action_mcp/client/json_rpc_handler.rb +9 -9
- data/lib/action_mcp/client/jwt_client_provider.rb +134 -0
- data/lib/action_mcp/configuration.rb +90 -11
- data/lib/action_mcp/engine.rb +26 -1
- data/lib/action_mcp/filtered_logger.rb +32 -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/prompt.rb +14 -0
- data/lib/action_mcp/registry_base.rb +25 -4
- data/lib/action_mcp/resource_response.rb +110 -0
- data/lib/action_mcp/resource_template.rb +30 -2
- data/lib/action_mcp/server/capabilities.rb +3 -14
- data/lib/action_mcp/server/memory_session.rb +0 -1
- data/lib/action_mcp/server/prompts.rb +8 -1
- data/lib/action_mcp/server/resources.rb +9 -6
- data/lib/action_mcp/server/tools.rb +41 -20
- data/lib/action_mcp/server.rb +6 -3
- data/lib/action_mcp/sse_listener.rb +0 -7
- data/lib/action_mcp/test_helper.rb +5 -0
- data/lib/action_mcp/tool.rb +108 -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
- data/lib/tasks/action_mcp_tasks.rake +238 -0
- metadata +11 -1
@@ -26,6 +26,8 @@ module ActionMCP
|
|
26
26
|
|
27
27
|
def abstract!
|
28
28
|
@abstract = true
|
29
|
+
# Unregister from the appropriate registry if already registered
|
30
|
+
ActionMCP::ResourceTemplatesRegistry.unregister(self) if ActionMCP::ResourceTemplatesRegistry.items.values.include?(self)
|
29
31
|
end
|
30
32
|
|
31
33
|
def inherited(subclass)
|
@@ -33,6 +35,11 @@ module ActionMCP
|
|
33
35
|
subclass.instance_variable_set(:@abstract, false)
|
34
36
|
# Create a copy of validation requirements for subclasses
|
35
37
|
subclass.instance_variable_set(:@required_parameters, [])
|
38
|
+
|
39
|
+
# Run the ActiveSupport load hook when a resource template is defined
|
40
|
+
subclass.class_eval do
|
41
|
+
ActiveSupport.run_load_hooks(:action_mcp_resource_template, subclass)
|
42
|
+
end
|
36
43
|
end
|
37
44
|
|
38
45
|
# Track required parameters for validation
|
@@ -109,6 +116,7 @@ module ActionMCP
|
|
109
116
|
end
|
110
117
|
|
111
118
|
def capability_name
|
119
|
+
return "" if name.nil?
|
112
120
|
@capability_name ||= name.demodulize.underscore.sub(/_template$/, "")
|
113
121
|
end
|
114
122
|
|
@@ -245,9 +253,29 @@ module ActionMCP
|
|
245
253
|
end
|
246
254
|
|
247
255
|
def call
|
248
|
-
|
249
|
-
|
256
|
+
@response = ResourceResponse.new
|
257
|
+
|
258
|
+
# Validate parameters first
|
259
|
+
unless valid?
|
260
|
+
missing_params = errors.full_messages
|
261
|
+
@response.mark_as_parameter_validation_failed!(missing_params, "template://#{self.class.name}")
|
262
|
+
return @response
|
263
|
+
end
|
264
|
+
|
265
|
+
begin
|
266
|
+
run_callbacks :resolve do
|
267
|
+
result = resolve
|
268
|
+
if result.nil?
|
269
|
+
@response.mark_as_not_found!("template://#{self.class.name}")
|
270
|
+
else
|
271
|
+
@response.add_content(result)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
rescue StandardError => e
|
275
|
+
@response.mark_as_resolution_failed!("template://#{self.class.name}", e.message)
|
250
276
|
end
|
277
|
+
|
278
|
+
@response
|
251
279
|
end
|
252
280
|
end
|
253
281
|
end
|
@@ -18,7 +18,7 @@ module ActionMCP
|
|
18
18
|
unless client_protocol_version.is_a?(String) && client_protocol_version.present?
|
19
19
|
return send_jsonrpc_error(request_id, :invalid_params, "Missing or invalid 'protocolVersion'")
|
20
20
|
end
|
21
|
-
unless ActionMCP
|
21
|
+
unless ActionMCP::SUPPORTED_VERSIONS.include?(client_protocol_version)
|
22
22
|
error_message = "Unsupported protocol version. Client requested '#{client_protocol_version}' but server supports #{ActionMCP::SUPPORTED_VERSIONS.join(', ')}"
|
23
23
|
error_data = {
|
24
24
|
supported: ActionMCP::SUPPORTED_VERSIONS,
|
@@ -40,18 +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
|
-
capabilities_payload[:protocolVersion] =
|
48
|
-
ActionMCP::LATEST_VERSION
|
49
|
-
else
|
50
|
-
client_protocol_version
|
51
|
-
end
|
46
|
+
capabilities_payload[:protocolVersion] = client_protocol_version
|
52
47
|
return send_jsonrpc_response(request_id, result: capabilities_payload)
|
53
|
-
else
|
54
|
-
Rails.logger.warn("Session #{session_id} not found or not initialized, creating new session")
|
55
48
|
end
|
56
49
|
end
|
57
50
|
|
@@ -65,11 +58,7 @@ module ActionMCP
|
|
65
58
|
end
|
66
59
|
|
67
60
|
capabilities_payload = session.server_capabilities_payload
|
68
|
-
capabilities_payload[:protocolVersion] =
|
69
|
-
ActionMCP::LATEST_VERSION
|
70
|
-
else
|
71
|
-
client_protocol_version
|
72
|
-
end
|
61
|
+
capabilities_payload[:protocolVersion] = client_protocol_version
|
73
62
|
|
74
63
|
send_jsonrpc_response(request_id, result: capabilities_payload)
|
75
64
|
end
|
@@ -18,7 +18,14 @@ module ActionMCP
|
|
18
18
|
prompt = prompt_class.new(params)
|
19
19
|
prompt.with_context({ session: session })
|
20
20
|
|
21
|
-
|
21
|
+
# Wrap prompt execution with Rails reloader for development
|
22
|
+
result = if Rails.env.development? && defined?(Rails.application.reloader)
|
23
|
+
Rails.application.reloader.wrap do
|
24
|
+
prompt.call
|
25
|
+
end
|
26
|
+
else
|
27
|
+
prompt.call
|
28
|
+
end
|
22
29
|
|
23
30
|
if result.is_error
|
24
31
|
send_jsonrpc_response(request_id, error: result)
|
@@ -50,12 +50,15 @@ module ActionMCP
|
|
50
50
|
record = template.process(params[:uri])
|
51
51
|
record.with_context({ session: session })
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
response = record.call
|
54
|
+
|
55
|
+
if response.error?
|
56
|
+
# Convert ResourceResponse errors to JSON-RPC errors
|
57
|
+
error_info = response.to_h
|
58
|
+
send_jsonrpc_error(id, error_info[:code], error_info[:message], error_info[:data])
|
57
59
|
else
|
58
|
-
|
60
|
+
# Handle successful response - ResourceResponse.contents is already an array
|
61
|
+
send_jsonrpc_response(id, result: { contents: response.contents.map(&:to_h) })
|
59
62
|
end
|
60
63
|
else
|
61
64
|
send_jsonrpc_error(id, :invalid_params, "Invalid resource URI")
|
@@ -72,7 +75,7 @@ module ActionMCP
|
|
72
75
|
# @example Output:
|
73
76
|
# # Logs: "Registered Resource Templates: ["db://{table}", "file://{path}"]"
|
74
77
|
def log_resource_templates
|
75
|
-
|
78
|
+
# Resource templates: #{ActionMCP::ResourceTemplatesRegistry.resource_templates.keys}
|
76
79
|
end
|
77
80
|
end
|
78
81
|
end
|
@@ -18,7 +18,9 @@ module ActionMCP
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# Use session's registered tools instead of global registry
|
21
|
-
|
21
|
+
registered_tools = session.registered_tools
|
22
|
+
|
23
|
+
tools = registered_tools.map do |tool_class|
|
22
24
|
tool_class.to_h(protocol_version: protocol_version)
|
23
25
|
end
|
24
26
|
|
@@ -39,28 +41,47 @@ module ActionMCP
|
|
39
41
|
tool_class = session.registered_tools.find { |t| t.tool_name == tool_name }
|
40
42
|
|
41
43
|
if tool_class
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
+
}
|
51
55
|
}
|
52
|
-
}
|
53
|
-
|
56
|
+
})
|
57
|
+
|
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
|
54
63
|
|
55
|
-
|
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
|
71
|
+
tool.call
|
72
|
+
end
|
56
73
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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)
|
64
85
|
end
|
65
86
|
else
|
66
87
|
send_jsonrpc_error(request_id, :method_not_found, "Tool '#{tool_name}' not available in this session")
|
data/lib/action_mcp/server.rb
CHANGED
@@ -30,9 +30,12 @@ module ActionMCP
|
|
30
30
|
|
31
31
|
# Access the session store
|
32
32
|
def session_store
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
current_type = ActionMCP.configuration.server_session_store_type
|
34
|
+
if @session_store.nil? || @session_store_type != current_type
|
35
|
+
@session_store_type = current_type
|
36
|
+
@session_store = SessionStoreFactory.create(current_type)
|
37
|
+
end
|
38
|
+
@session_store
|
36
39
|
end
|
37
40
|
|
38
41
|
# Available pubsub adapter types
|
@@ -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
@@ -43,6 +43,7 @@ module ActionMCP
|
|
43
43
|
#
|
44
44
|
# @return [String] The default tool name.
|
45
45
|
def self.default_tool_name
|
46
|
+
return "" if name.nil?
|
46
47
|
name.demodulize.underscore.sub(/_tool$/, "")
|
47
48
|
end
|
48
49
|
|
@@ -53,6 +54,19 @@ module ActionMCP
|
|
53
54
|
:tool
|
54
55
|
end
|
55
56
|
|
57
|
+
def unregister_from_registry
|
58
|
+
ActionMCP::ToolsRegistry.unregister(self) if ActionMCP::ToolsRegistry.items.values.include?(self)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Hook called when a class inherits from Tool
|
62
|
+
def inherited(subclass)
|
63
|
+
super
|
64
|
+
# Run the ActiveSupport load hook when a tool is defined
|
65
|
+
subclass.class_eval do
|
66
|
+
ActiveSupport.run_load_hooks(:action_mcp_tool, subclass)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
56
70
|
def annotate(key, value)
|
57
71
|
self._annotations = _annotations.merge(key.to_s => value)
|
58
72
|
end
|
@@ -162,7 +176,7 @@ module ActionMCP
|
|
162
176
|
|
163
177
|
return unless %w[number integer].include?(type)
|
164
178
|
|
165
|
-
validates prop_name, numericality: true, allow_nil:
|
179
|
+
validates prop_name, numericality: true, allow_nil: !required
|
166
180
|
end
|
167
181
|
|
168
182
|
# --------------------------------------------------------------------------
|
@@ -243,6 +257,13 @@ module ActionMCP
|
|
243
257
|
# Instance Methods
|
244
258
|
# --------------------------------------------------------------------------
|
245
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
|
+
|
246
267
|
# Public entry point for executing the tool
|
247
268
|
# Returns an array of Content objects collected from render calls
|
248
269
|
def call
|
@@ -259,14 +280,16 @@ module ActionMCP
|
|
259
280
|
@response.mark_as_error!(:internal_error, message: e.message)
|
260
281
|
end
|
261
282
|
else
|
262
|
-
@response.mark_as_error!(:
|
283
|
+
@response.mark_as_error!(:invalid_params,
|
263
284
|
message: "Invalid input",
|
264
285
|
data: errors.full_messages)
|
265
286
|
end
|
266
287
|
|
267
288
|
# If callbacks halted execution (`performed` still false) and
|
268
|
-
# nothing else marked an error, surface it as
|
269
|
-
|
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
|
270
293
|
|
271
294
|
@response
|
272
295
|
end
|
@@ -348,5 +371,86 @@ module ActionMCP
|
|
348
371
|
end
|
349
372
|
|
350
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
|
351
455
|
end
|
352
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
|