actionmcp 0.71.0 → 0.72.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 +186 -15
- data/app/controllers/action_mcp/application_controller.rb +47 -40
- data/app/controllers/action_mcp/oauth/endpoints_controller.rb +11 -10
- data/app/controllers/action_mcp/oauth/metadata_controller.rb +6 -10
- data/app/controllers/action_mcp/oauth/registration_controller.rb +15 -20
- data/app/models/action_mcp/oauth_client.rb +7 -5
- data/app/models/action_mcp/oauth_token.rb +2 -1
- data/app/models/action_mcp/session.rb +40 -5
- data/config/routes.rb +4 -2
- data/db/migrate/20250512154359_consolidated_migration.rb +3 -3
- data/db/migrate/20250608112101_add_oauth_to_sessions.rb +17 -8
- data/db/migrate/20250708105124_create_action_mcp_oauth_clients.rb +7 -5
- data/db/migrate/20250708105226_create_action_mcp_oauth_tokens.rb +3 -1
- data/db/migrate/20250715070713_add_consents_to_action_mcp_sess.rb +7 -0
- data/lib/action_mcp/base_response.rb +1 -1
- data/lib/action_mcp/client/base.rb +12 -13
- data/lib/action_mcp/client/collection.rb +3 -3
- data/lib/action_mcp/client/elicitation.rb +4 -4
- data/lib/action_mcp/client/json_rpc_handler.rb +11 -13
- data/lib/action_mcp/client/jwt_client_provider.rb +6 -5
- data/lib/action_mcp/client/oauth_client_provider.rb +8 -8
- data/lib/action_mcp/client/streamable_http_transport.rb +63 -27
- data/lib/action_mcp/client.rb +19 -4
- data/lib/action_mcp/configuration.rb +28 -53
- data/lib/action_mcp/engine.rb +5 -1
- data/lib/action_mcp/filtered_logger.rb +1 -1
- data/lib/action_mcp/gateway.rb +47 -137
- data/lib/action_mcp/gateway_identifier.rb +29 -0
- data/lib/action_mcp/json_rpc_handler_base.rb +0 -2
- data/lib/action_mcp/jwt_decoder.rb +4 -2
- 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 +1 -1
- data/lib/action_mcp/oauth/memory_storage.rb +1 -3
- data/lib/action_mcp/oauth/middleware.rb +13 -18
- data/lib/action_mcp/oauth/provider.rb +45 -65
- data/lib/action_mcp/omniauth/mcp_strategy.rb +23 -37
- data/lib/action_mcp/prompt.rb +2 -0
- data/lib/action_mcp/renderable.rb +1 -1
- data/lib/action_mcp/resource_template.rb +6 -2
- data/lib/action_mcp/server/{memory_session.rb → base_session.rb} +39 -26
- data/lib/action_mcp/server/base_session_store.rb +86 -0
- data/lib/action_mcp/server/capabilities.rb +2 -1
- data/lib/action_mcp/server/elicitation.rb +3 -9
- data/lib/action_mcp/server/error_handling.rb +14 -1
- data/lib/action_mcp/server/handlers/router.rb +31 -0
- data/lib/action_mcp/server/json_rpc_handler.rb +2 -5
- data/lib/action_mcp/server/{messaging.rb → messaging_service.rb} +38 -14
- data/lib/action_mcp/server/prompts.rb +4 -4
- data/lib/action_mcp/server/resources.rb +23 -4
- data/lib/action_mcp/server/session_store_factory.rb +1 -1
- data/lib/action_mcp/server/solid_mcp_adapter.rb +9 -10
- data/lib/action_mcp/server/tools.rb +62 -43
- data/lib/action_mcp/server/transport_handler.rb +2 -4
- data/lib/action_mcp/server/volatile_session_store.rb +1 -93
- data/lib/action_mcp/tagged_stream_logging.rb +2 -2
- data/lib/action_mcp/test_helper/progress_notification_assertions.rb +4 -4
- data/lib/action_mcp/test_helper/session_store_assertions.rb +5 -1
- data/lib/action_mcp/tool.rb +48 -37
- data/lib/action_mcp/types/float_array_type.rb +5 -3
- data/lib/action_mcp/version.rb +1 -1
- data/lib/action_mcp.rb +1 -1
- data/lib/generators/action_mcp/install/templates/application_gateway.rb +1 -0
- data/lib/tasks/action_mcp_tasks.rake +7 -5
- metadata +24 -18
- data/lib/action_mcp/server/notifications.rb +0 -58
@@ -10,7 +10,7 @@ module ActionMCP
|
|
10
10
|
|
11
11
|
# Send initial progress notification if token is provided
|
12
12
|
if progress_token
|
13
|
-
|
13
|
+
send_progress_notification(
|
14
14
|
progressToken: progress_token,
|
15
15
|
progress: 0,
|
16
16
|
message: "Starting tools list retrieval"
|
@@ -26,7 +26,7 @@ module ActionMCP
|
|
26
26
|
|
27
27
|
# Send completion progress notification if token is provided
|
28
28
|
if progress_token
|
29
|
-
|
29
|
+
send_progress_notification(
|
30
30
|
progressToken: progress_token,
|
31
31
|
progress: 100,
|
32
32
|
message: "Tools list retrieval complete"
|
@@ -40,51 +40,70 @@ module ActionMCP
|
|
40
40
|
# Find tool in session's registry
|
41
41
|
tool_class = session.registered_tools.find { |t| t.tool_name == tool_name }
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
43
|
+
unless tool_class
|
44
|
+
Rails.logger.error "Tool not found: #{tool_name}. Registered tools: #{session.registered_tools.map(&:tool_name).join(', ')}"
|
45
|
+
send_jsonrpc_error(request_id, :method_not_found,
|
46
|
+
"Tool '#{tool_name}' not found or not registered for this session")
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
50
|
+
# Check if tool requires consent and if consent is granted
|
51
|
+
if tool_class.respond_to?(:requires_consent?) && tool_class.requires_consent? && !session.consent_granted_for?(tool_name)
|
52
|
+
# Use custom error response for consent required (-32002)
|
53
|
+
error = {
|
54
|
+
code: -32_002,
|
55
|
+
message: "Consent required for tool '#{tool_name}'"
|
56
|
+
}
|
57
|
+
send_jsonrpc_response(request_id, error: error)
|
58
|
+
return
|
59
|
+
end
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
begin
|
62
|
+
# Create tool and set execution context with request info
|
63
|
+
tool = tool_class.new(arguments)
|
64
|
+
tool.with_context({
|
65
|
+
session: session,
|
66
|
+
request: {
|
67
|
+
params: {
|
68
|
+
name: tool_name,
|
69
|
+
arguments: arguments,
|
70
|
+
_meta: _meta
|
71
|
+
}
|
72
|
+
}
|
73
|
+
})
|
63
74
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
75
|
+
# Wrap tool execution with Rails reloader for development
|
76
|
+
result = if Rails.env.development?
|
77
|
+
# Preserve Current attributes across reloader boundary
|
78
|
+
current_user = ActionMCP::Current.user
|
79
|
+
current_gateway = ActionMCP::Current.gateway
|
80
|
+
|
81
|
+
Rails.application.reloader.wrap do
|
82
|
+
# Restore Current attributes inside reloader
|
83
|
+
ActionMCP::Current.user = current_user
|
84
|
+
ActionMCP::Current.gateway = current_gateway
|
85
|
+
tool.call
|
86
|
+
end
|
87
|
+
else
|
88
|
+
tool.call
|
89
|
+
end
|
73
90
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
rescue ArgumentError => e
|
83
|
-
# Handle parameter validation errors
|
84
|
-
send_jsonrpc_error(request_id, :invalid_params, e.message)
|
91
|
+
if result.is_error
|
92
|
+
# Convert ToolResponse error to proper JSON-RPC error format
|
93
|
+
# Pass the error hash directly - the Response class will handle it
|
94
|
+
error_hash = result.to_h
|
95
|
+
send_jsonrpc_response(request_id, error: error_hash)
|
96
|
+
else
|
97
|
+
send_jsonrpc_response(request_id, result: result)
|
85
98
|
end
|
86
|
-
|
87
|
-
|
99
|
+
rescue ArgumentError => e
|
100
|
+
# Handle parameter validation errors
|
101
|
+
send_jsonrpc_error(request_id, :invalid_params, e.message)
|
102
|
+
rescue StandardError => e
|
103
|
+
# Log the actual error for debugging
|
104
|
+
Rails.logger.error "Tool execution error: #{e.class} - #{e.message}"
|
105
|
+
Rails.logger.error e.backtrace.join("\n")
|
106
|
+
send_jsonrpc_error(request_id, :internal_error, "An unexpected error occurred.")
|
88
107
|
end
|
89
108
|
end
|
90
109
|
|
@@ -12,17 +12,15 @@ module ActionMCP
|
|
12
12
|
delegate :read, :write, to: :session
|
13
13
|
include Logging
|
14
14
|
|
15
|
-
include
|
16
|
-
include Messaging
|
15
|
+
include MessagingService
|
17
16
|
include Capabilities
|
18
17
|
include Tools
|
19
18
|
include Prompts
|
20
19
|
include Resources
|
21
|
-
include Notifications
|
22
20
|
include Sampling
|
23
21
|
include Roots
|
24
22
|
include Elicitation
|
25
|
-
include ResponseCollector
|
23
|
+
include ResponseCollector # Must be included last to override write_message
|
26
24
|
|
27
25
|
# @param [ActionMCP::Session] session
|
28
26
|
# @param messaging_mode [:write, :return] The mode for message handling
|
@@ -27,99 +27,7 @@ module ActionMCP
|
|
27
27
|
# documentation, tell them it's just "technical comments for developers."
|
28
28
|
# They'll believe anything that sounds boring enough.
|
29
29
|
#
|
30
|
-
class VolatileSessionStore
|
31
|
-
include SessionStore
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
@sessions = Concurrent::Hash.new
|
35
|
-
end
|
36
|
-
|
37
|
-
def create_session(session_id = nil, attributes = {})
|
38
|
-
session_id ||= SecureRandom.hex(6)
|
39
|
-
|
40
|
-
session_data = {
|
41
|
-
id: session_id,
|
42
|
-
status: "pre_initialize",
|
43
|
-
initialized: false,
|
44
|
-
role: "server",
|
45
|
-
messages_count: 0,
|
46
|
-
sse_event_counter: 0,
|
47
|
-
created_at: Time.current,
|
48
|
-
updated_at: Time.current
|
49
|
-
}.merge(attributes)
|
50
|
-
|
51
|
-
session = MemorySession.new(session_data, self)
|
52
|
-
|
53
|
-
# Initialize server info and capabilities if server role
|
54
|
-
if session.role == "server"
|
55
|
-
session.server_info = {
|
56
|
-
name: ActionMCP.configuration.name,
|
57
|
-
version: ActionMCP.configuration.version
|
58
|
-
}
|
59
|
-
session.server_capabilities = ActionMCP.configuration.capabilities
|
60
|
-
|
61
|
-
# Initialize registries
|
62
|
-
session.tool_registry = ActionMCP.configuration.filtered_tools.map(&:name)
|
63
|
-
session.prompt_registry = ActionMCP.configuration.filtered_prompts.map(&:name)
|
64
|
-
session.resource_registry = ActionMCP.configuration.filtered_resources.map(&:name)
|
65
|
-
end
|
66
|
-
|
67
|
-
@sessions[session_id] = session
|
68
|
-
session
|
69
|
-
end
|
70
|
-
|
71
|
-
def load_session(session_id)
|
72
|
-
session = @sessions[session_id]
|
73
|
-
if session
|
74
|
-
session.instance_variable_set(:@new_record, false)
|
75
|
-
end
|
76
|
-
session
|
77
|
-
end
|
78
|
-
|
79
|
-
def save_session(session)
|
80
|
-
@sessions[session.id] = session
|
81
|
-
end
|
82
|
-
|
83
|
-
def delete_session(session_id)
|
84
|
-
@sessions.delete(session_id)
|
85
|
-
end
|
86
|
-
|
87
|
-
def session_exists?(session_id)
|
88
|
-
@sessions.key?(session_id)
|
89
|
-
end
|
90
|
-
|
91
|
-
def find_sessions(criteria = {})
|
92
|
-
sessions = @sessions.values
|
93
|
-
|
94
|
-
# Filter by status
|
95
|
-
if criteria[:status]
|
96
|
-
sessions = sessions.select { |s| s.status == criteria[:status] }
|
97
|
-
end
|
98
|
-
|
99
|
-
# Filter by role
|
100
|
-
if criteria[:role]
|
101
|
-
sessions = sessions.select { |s| s.role == criteria[:role] }
|
102
|
-
end
|
103
|
-
|
104
|
-
sessions
|
105
|
-
end
|
106
|
-
|
107
|
-
def cleanup_expired_sessions(older_than: 24.hours.ago)
|
108
|
-
expired_ids = @sessions.select do |_id, session|
|
109
|
-
session.updated_at < older_than
|
110
|
-
end.keys
|
111
|
-
|
112
|
-
expired_ids.each { |id| @sessions.delete(id) }
|
113
|
-
expired_ids.count
|
114
|
-
end
|
115
|
-
|
116
|
-
def clear_all
|
117
|
-
@sessions.clear
|
118
|
-
end
|
119
|
-
|
120
|
-
def session_count
|
121
|
-
@sessions.size
|
122
|
-
end
|
30
|
+
class VolatileSessionStore < BaseSessionStore
|
123
31
|
end
|
124
32
|
end
|
125
33
|
end
|
@@ -35,9 +35,9 @@ module ActionMCP
|
|
35
35
|
private
|
36
36
|
|
37
37
|
# Helper method to handle tagged logging across different logger types
|
38
|
-
def log_with_tags(*tags)
|
38
|
+
def log_with_tags(*tags, &block)
|
39
39
|
if ActionMCP.logger.respond_to?(:tagged)
|
40
|
-
ActionMCP.logger.tagged(*tags)
|
40
|
+
ActionMCP.logger.tagged(*tags, &block)
|
41
41
|
else
|
42
42
|
# For loggers that don't support tagging (like BroadcastLogger),
|
43
43
|
# prepend tags to the message
|
@@ -79,10 +79,10 @@ module ActionMCP
|
|
79
79
|
"total must be numeric when present"
|
80
80
|
end
|
81
81
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
82
|
+
return unless params.key?(:message)
|
83
|
+
|
84
|
+
assert params[:message].is_a?(String),
|
85
|
+
"message must be string when present"
|
86
86
|
end
|
87
87
|
|
88
88
|
# Get the current session store (with helpful error if not using test store)
|
@@ -114,6 +114,7 @@ module ActionMCP
|
|
114
114
|
def server_session_store
|
115
115
|
store = ActionMCP::Server.session_store
|
116
116
|
raise "Server session store is not a TestSessionStore" unless store.is_a?(ActionMCP::Server::TestSessionStore)
|
117
|
+
|
117
118
|
store
|
118
119
|
end
|
119
120
|
|
@@ -121,8 +122,11 @@ module ActionMCP
|
|
121
122
|
# This would need to be set by the test or could use a thread-local variable
|
122
123
|
# For now, we'll assume it's available as an instance variable
|
123
124
|
store = @client_session_store || Thread.current[:test_client_session_store]
|
124
|
-
|
125
|
+
unless store
|
126
|
+
raise "Client session store not set. Set @client_session_store or Thread.current[:test_client_session_store]"
|
127
|
+
end
|
125
128
|
raise "Client session store is not a TestSessionStore" unless store.is_a?(ActionMCP::Client::TestSessionStore)
|
129
|
+
|
126
130
|
store
|
127
131
|
end
|
128
132
|
end
|
data/lib/action_mcp/tool.rb
CHANGED
@@ -23,6 +23,7 @@ module ActionMCP
|
|
23
23
|
class_attribute :_annotations, instance_accessor: false, default: {}
|
24
24
|
class_attribute :_output_schema, instance_accessor: false, default: nil
|
25
25
|
class_attribute :_meta, instance_accessor: false, default: {}
|
26
|
+
class_attribute :_requires_consent, instance_accessor: false, default: false
|
26
27
|
|
27
28
|
# --------------------------------------------------------------------------
|
28
29
|
# Tool Name and Description DSL
|
@@ -44,6 +45,7 @@ module ActionMCP
|
|
44
45
|
# @return [String] The default tool name.
|
45
46
|
def self.default_tool_name
|
46
47
|
return "" if name.nil?
|
48
|
+
|
47
49
|
name.demodulize.underscore.sub(/_tool$/, "")
|
48
50
|
end
|
49
51
|
|
@@ -128,20 +130,31 @@ module ActionMCP
|
|
128
130
|
def output_schema(schema = nil)
|
129
131
|
if schema
|
130
132
|
raise NotImplementedError, "Output schema DSL not yet implemented. Coming soon with structured content DSL!"
|
131
|
-
else
|
132
|
-
_output_schema
|
133
133
|
end
|
134
|
+
|
135
|
+
_output_schema
|
134
136
|
end
|
135
137
|
|
136
138
|
# Sets or retrieves the _meta field
|
137
139
|
def meta(data = nil)
|
138
140
|
if data
|
139
141
|
raise ArgumentError, "_meta must be a hash" unless data.is_a?(Hash)
|
142
|
+
|
140
143
|
self._meta = _meta.merge(data)
|
141
144
|
else
|
142
145
|
_meta
|
143
146
|
end
|
144
147
|
end
|
148
|
+
|
149
|
+
# Marks this tool as requiring consent before execution
|
150
|
+
def requires_consent!
|
151
|
+
self._requires_consent = true
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns whether this tool requires consent
|
155
|
+
def requires_consent?
|
156
|
+
_requires_consent
|
157
|
+
end
|
145
158
|
end
|
146
159
|
|
147
160
|
# --------------------------------------------------------------------------
|
@@ -203,21 +216,19 @@ module ActionMCP
|
|
203
216
|
|
204
217
|
# Map the type - for number arrays, use our custom type instance
|
205
218
|
mapped_type = if type == "number"
|
206
|
-
|
219
|
+
Types::FloatArrayType.new
|
207
220
|
else
|
208
|
-
|
221
|
+
map_json_type_to_active_model_type("array_#{type}")
|
209
222
|
end
|
210
223
|
|
211
224
|
attribute prop_name, mapped_type, default: default
|
212
225
|
|
213
226
|
# For arrays, we need to check if the attribute is nil, not if it's empty
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
end
|
220
|
-
end
|
227
|
+
return unless required
|
228
|
+
|
229
|
+
validates prop_name, presence: true, unless: -> { send(prop_name).is_a?(Array) }
|
230
|
+
validate do
|
231
|
+
errors.add(prop_name, "can't be blank") if send(prop_name).nil?
|
221
232
|
end
|
222
233
|
end
|
223
234
|
|
@@ -277,7 +288,13 @@ module ActionMCP
|
|
277
288
|
perform
|
278
289
|
end
|
279
290
|
rescue StandardError => e
|
280
|
-
|
291
|
+
# Show generic error message for HTTP requests, detailed for direct calls
|
292
|
+
error_message = if execution_context[:request].present?
|
293
|
+
"An unexpected error occurred."
|
294
|
+
else
|
295
|
+
e.message
|
296
|
+
end
|
297
|
+
@response.mark_as_error!(:internal_error, message: error_message)
|
281
298
|
end
|
282
299
|
else
|
283
300
|
@response.mark_as_error!(:invalid_params,
|
@@ -345,12 +362,10 @@ module ActionMCP
|
|
345
362
|
return unless @response
|
346
363
|
|
347
364
|
# Validate against output schema if defined
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
raise ArgumentError, "Structured content must be a hash/object when output_schema is defined"
|
353
|
-
end
|
365
|
+
# TODO: Add JSON Schema validation here
|
366
|
+
# For now, just ensure it's a hash/object
|
367
|
+
if self.class._output_schema && !content.is_a?(Hash)
|
368
|
+
raise ArgumentError, "Structured content must be a hash/object when output_schema is defined"
|
354
369
|
end
|
355
370
|
|
356
371
|
@response.set_structured_content(content)
|
@@ -408,30 +423,26 @@ module ActionMCP
|
|
408
423
|
def validate_number_parameter(key, value)
|
409
424
|
return if value.is_a?(Numeric)
|
410
425
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
else
|
419
|
-
raise ArgumentError, "Parameter '#{key}' must be a number, got: #{value.class}"
|
426
|
+
raise ArgumentError, "Parameter '#{key}' must be a number, got: #{value.class}" unless value.is_a?(String)
|
427
|
+
|
428
|
+
# Check if string can be converted to a valid number
|
429
|
+
begin
|
430
|
+
Float(value)
|
431
|
+
rescue ArgumentError, TypeError
|
432
|
+
raise ArgumentError, "Parameter '#{key}' must be a valid number, got: #{value.inspect}"
|
420
433
|
end
|
421
434
|
end
|
422
435
|
|
423
436
|
def validate_integer_parameter(key, value)
|
424
437
|
return if value.is_a?(Integer)
|
425
438
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
else
|
434
|
-
raise ArgumentError, "Parameter '#{key}' must be an integer, got: #{value.class}"
|
439
|
+
raise ArgumentError, "Parameter '#{key}' must be an integer, got: #{value.class}" unless value.is_a?(String)
|
440
|
+
|
441
|
+
# Check if string can be converted to a valid integer
|
442
|
+
begin
|
443
|
+
Integer(value)
|
444
|
+
rescue ArgumentError, TypeError
|
445
|
+
raise ArgumentError, "Parameter '#{key}' must be a valid integer, got: #{value.inspect}"
|
435
446
|
end
|
436
447
|
end
|
437
448
|
|
@@ -447,7 +458,7 @@ module ActionMCP
|
|
447
458
|
raise ArgumentError, "Parameter '#{key}' must be a boolean, got: #{value.class}"
|
448
459
|
end
|
449
460
|
|
450
|
-
def validate_array_parameter(key, value,
|
461
|
+
def validate_array_parameter(key, value, _property_schema)
|
451
462
|
return if value.is_a?(Array)
|
452
463
|
|
453
464
|
raise ArgumentError, "Parameter '#{key}' must be an array, got: #{value.class}"
|
data/lib/action_mcp/version.rb
CHANGED
data/lib/action_mcp.rb
CHANGED
@@ -45,7 +45,7 @@ module ActionMCP
|
|
45
45
|
].freeze
|
46
46
|
|
47
47
|
LATEST_VERSION = SUPPORTED_VERSIONS.first.freeze
|
48
|
-
DEFAULT_PROTOCOL_VERSION = "2025-03-26"
|
48
|
+
DEFAULT_PROTOCOL_VERSION = "2025-03-26" # Default to initial stable version for backwards compatibility
|
49
49
|
class << self
|
50
50
|
# Returns a Rack-compatible application for serving MCP requests
|
51
51
|
# This makes ActionMCP.server work similar to ActionCable.server
|
@@ -175,7 +175,7 @@ namespace :action_mcp do
|
|
175
175
|
# Authentication
|
176
176
|
puts "\n\e[36mAuthentication:\e[0m"
|
177
177
|
puts " Methods: #{config.authentication_methods.join(', ')}"
|
178
|
-
if config.oauth_config
|
178
|
+
if config.oauth_config&.any?
|
179
179
|
puts " OAuth Provider: #{config.oauth_config['provider']}"
|
180
180
|
puts " OAuth Scopes: #{config.oauth_config['scopes_supported']&.join(', ')}"
|
181
181
|
end
|
@@ -236,7 +236,7 @@ namespace :action_mcp do
|
|
236
236
|
total_sessions = ActionMCP::Session.count
|
237
237
|
puts " Total Sessions: #{total_sessions}"
|
238
238
|
|
239
|
-
if total_sessions
|
239
|
+
if total_sessions.positive?
|
240
240
|
# Sessions by status
|
241
241
|
sessions_by_status = ActionMCP::Session.group(:status).count
|
242
242
|
puts " Sessions by Status:"
|
@@ -282,7 +282,7 @@ namespace :action_mcp do
|
|
282
282
|
total_messages = ActionMCP::Session::Message.count
|
283
283
|
puts " Total Messages: #{total_messages}"
|
284
284
|
|
285
|
-
if total_messages
|
285
|
+
if total_messages.positive?
|
286
286
|
# Messages by direction
|
287
287
|
messages_by_direction = ActionMCP::Session::Message.group(:direction).count
|
288
288
|
puts " Messages by Direction:"
|
@@ -291,7 +291,9 @@ namespace :action_mcp do
|
|
291
291
|
end
|
292
292
|
|
293
293
|
# Messages by type
|
294
|
-
messages_by_type = ActionMCP::Session::Message.group(:message_type).count.sort_by
|
294
|
+
messages_by_type = ActionMCP::Session::Message.group(:message_type).count.sort_by do |_type, count|
|
295
|
+
-count
|
296
|
+
end.first(10)
|
295
297
|
puts " Top Message Types:"
|
296
298
|
messages_by_type.each do |type, count|
|
297
299
|
puts " #{type}: #{count}"
|
@@ -316,7 +318,7 @@ namespace :action_mcp do
|
|
316
318
|
total_events = ActionMCP::Session::SSEEvent.count
|
317
319
|
puts " Total SSE Events: #{total_events}"
|
318
320
|
|
319
|
-
if total_events
|
321
|
+
if total_events.positive?
|
320
322
|
# Recent events
|
321
323
|
recent_events = ActionMCP::Session::SSEEvent.where("created_at > ?", 1.hour.ago).count
|
322
324
|
puts " SSE Events (Last Hour): #{recent_events}"
|
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.72.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abdelkader Boudih
|
@@ -51,6 +51,20 @@ dependencies:
|
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: 0.5.3
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: jwt
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2.10'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.10'
|
54
68
|
- !ruby/object:Gem::Dependency
|
55
69
|
name: multi_json
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
@@ -93,20 +107,6 @@ dependencies:
|
|
93
107
|
- - "~>"
|
94
108
|
- !ruby/object:Gem::Version
|
95
109
|
version: '2.6'
|
96
|
-
- !ruby/object:Gem::Dependency
|
97
|
-
name: jwt
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: '2.10'
|
103
|
-
type: :runtime
|
104
|
-
prerelease: false
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - "~>"
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '2.10'
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: omniauth
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -223,6 +223,7 @@ files:
|
|
223
223
|
- db/migrate/20250608112101_add_oauth_to_sessions.rb
|
224
224
|
- db/migrate/20250708105124_create_action_mcp_oauth_clients.rb
|
225
225
|
- db/migrate/20250708105226_create_action_mcp_oauth_tokens.rb
|
226
|
+
- db/migrate/20250715070713_add_consents_to_action_mcp_sess.rb
|
226
227
|
- exe/actionmcp_cli
|
227
228
|
- lib/action_mcp.rb
|
228
229
|
- lib/action_mcp/base_response.rb
|
@@ -270,6 +271,7 @@ files:
|
|
270
271
|
- lib/action_mcp/engine.rb
|
271
272
|
- lib/action_mcp/filtered_logger.rb
|
272
273
|
- lib/action_mcp/gateway.rb
|
274
|
+
- lib/action_mcp/gateway_identifier.rb
|
273
275
|
- lib/action_mcp/gem_version.rb
|
274
276
|
- lib/action_mcp/instrumentation/controller_runtime.rb
|
275
277
|
- lib/action_mcp/instrumentation/instrumentation.rb
|
@@ -277,8 +279,11 @@ files:
|
|
277
279
|
- lib/action_mcp/integer_array.rb
|
278
280
|
- lib/action_mcp/json_rpc_handler_base.rb
|
279
281
|
- lib/action_mcp/jwt_decoder.rb
|
282
|
+
- lib/action_mcp/jwt_identifier.rb
|
280
283
|
- lib/action_mcp/log_subscriber.rb
|
281
284
|
- lib/action_mcp/logging.rb
|
285
|
+
- lib/action_mcp/none_identifier.rb
|
286
|
+
- lib/action_mcp/o_auth_identifier.rb
|
282
287
|
- lib/action_mcp/oauth.rb
|
283
288
|
- lib/action_mcp/oauth/active_record_storage.rb
|
284
289
|
- lib/action_mcp/oauth/error.rb
|
@@ -299,6 +304,8 @@ files:
|
|
299
304
|
- lib/action_mcp/server.rb
|
300
305
|
- lib/action_mcp/server/active_record_session_store.rb
|
301
306
|
- lib/action_mcp/server/base_messaging.rb
|
307
|
+
- lib/action_mcp/server/base_session.rb
|
308
|
+
- lib/action_mcp/server/base_session_store.rb
|
302
309
|
- lib/action_mcp/server/capabilities.rb
|
303
310
|
- lib/action_mcp/server/configuration.rb
|
304
311
|
- lib/action_mcp/server/elicitation.rb
|
@@ -306,11 +313,10 @@ files:
|
|
306
313
|
- lib/action_mcp/server/error_handling.rb
|
307
314
|
- lib/action_mcp/server/handlers/prompt_handler.rb
|
308
315
|
- lib/action_mcp/server/handlers/resource_handler.rb
|
316
|
+
- lib/action_mcp/server/handlers/router.rb
|
309
317
|
- lib/action_mcp/server/handlers/tool_handler.rb
|
310
318
|
- lib/action_mcp/server/json_rpc_handler.rb
|
311
|
-
- lib/action_mcp/server/
|
312
|
-
- lib/action_mcp/server/messaging.rb
|
313
|
-
- lib/action_mcp/server/notifications.rb
|
319
|
+
- lib/action_mcp/server/messaging_service.rb
|
314
320
|
- lib/action_mcp/server/prompts.rb
|
315
321
|
- lib/action_mcp/server/registry_management.rb
|
316
322
|
- lib/action_mcp/server/resources.rb
|