actionmcp 0.52.0 → 0.52.2

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.
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Server
5
+ class SessionStoreFactory
6
+ def self.create(type = nil, **options)
7
+ type ||= default_type
8
+
9
+ case type.to_sym
10
+ when :volatile, :memory
11
+ VolatileSessionStore.new
12
+ when :active_record, :persistent
13
+ ActiveRecordSessionStore.new
14
+ when :test
15
+ TestSessionStore.new
16
+ else
17
+ raise ArgumentError, "Unknown session store type: #{type}"
18
+ end
19
+ end
20
+
21
+ def self.default_type
22
+ if Rails.env.test?
23
+ :volatile # Use volatile for tests unless explicitly using :test
24
+ elsif Rails.env.production?
25
+ :active_record
26
+ else
27
+ :volatile
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Server
5
+ # Test session store that tracks all operations for assertions
6
+ class TestSessionStore < VolatileSessionStore
7
+ attr_reader :operations, :created_sessions, :loaded_sessions,
8
+ :saved_sessions, :deleted_sessions, :notifications_sent
9
+
10
+ def initialize
11
+ super
12
+ @operations = Concurrent::Array.new
13
+ @created_sessions = Concurrent::Array.new
14
+ @loaded_sessions = Concurrent::Array.new
15
+ @saved_sessions = Concurrent::Array.new
16
+ @deleted_sessions = Concurrent::Array.new
17
+ @notifications_sent = Concurrent::Array.new
18
+ @notification_callbacks = Concurrent::Array.new
19
+ end
20
+
21
+ def create_session(session_id = nil, attributes = {})
22
+ session = super
23
+ @operations << { type: :create, session_id: session.id, attributes: attributes }
24
+ @created_sessions << session.id
25
+
26
+ # Hook into the session's write method to capture notifications
27
+ intercept_session_write(session)
28
+
29
+ session
30
+ end
31
+
32
+ def load_session(session_id)
33
+ session = super
34
+ @operations << { type: :load, session_id: session_id, found: !session.nil? }
35
+ @loaded_sessions << session_id if session
36
+
37
+ # Hook into the session's write method to capture notifications
38
+ intercept_session_write(session) if session
39
+
40
+ session
41
+ end
42
+
43
+ def save_session(session)
44
+ super
45
+ @operations << { type: :save, session_id: session.id }
46
+ @saved_sessions << session.id
47
+ end
48
+
49
+ def delete_session(session_id)
50
+ result = super
51
+ @operations << { type: :delete, session_id: session_id }
52
+ @deleted_sessions << session_id
53
+ result
54
+ end
55
+
56
+ def cleanup_expired_sessions(older_than: 24.hours.ago)
57
+ count = super
58
+ @operations << { type: :cleanup, older_than: older_than, count: count }
59
+ count
60
+ end
61
+
62
+ # Test helper methods
63
+ def session_created?(session_id)
64
+ @created_sessions.include?(session_id)
65
+ end
66
+
67
+ def session_loaded?(session_id)
68
+ @loaded_sessions.include?(session_id)
69
+ end
70
+
71
+ def session_saved?(session_id)
72
+ @saved_sessions.include?(session_id)
73
+ end
74
+
75
+ def session_deleted?(session_id)
76
+ @deleted_sessions.include?(session_id)
77
+ end
78
+
79
+ def operation_count(type = nil)
80
+ if type
81
+ @operations.count { |op| op[:type] == type }
82
+ else
83
+ @operations.size
84
+ end
85
+ end
86
+
87
+ # Notification tracking methods
88
+ def track_notification(notification)
89
+ @notifications_sent << notification
90
+ @notification_callbacks.each { |cb| cb.call(notification) }
91
+ end
92
+
93
+ def on_notification(&block)
94
+ @notification_callbacks << block
95
+ end
96
+
97
+ def notifications_for_token(token)
98
+ @notifications_sent.select do |n|
99
+ n.params[:progressToken] == token
100
+ end
101
+ end
102
+
103
+ def clear_notifications
104
+ @notifications_sent.clear
105
+ end
106
+
107
+ def reset_tracking!
108
+ @operations.clear
109
+ @created_sessions.clear
110
+ @loaded_sessions.clear
111
+ @saved_sessions.clear
112
+ @deleted_sessions.clear
113
+ @notifications_sent.clear
114
+ @notification_callbacks.clear
115
+ end
116
+
117
+ private
118
+
119
+ def intercept_session_write(session)
120
+ return unless session
121
+
122
+ # Skip if already intercepted
123
+ return if session.singleton_methods.include?(:write)
124
+
125
+ test_store = self
126
+
127
+ # Intercept write method to capture all notifications
128
+ original_write = session.method(:write)
129
+
130
+ session.define_singleton_method(:write) do |data|
131
+ # Track progress notifications before calling original write
132
+ if data.is_a?(JSON_RPC::Notification) && data.method == "notifications/progress"
133
+ test_store.track_notification(data)
134
+ end
135
+
136
+ original_write.call(data)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Server
5
+ # Volatile session store for development (data lost on restart)
6
+ class VolatileSessionStore
7
+ include SessionStore
8
+
9
+ def initialize
10
+ @sessions = Concurrent::Hash.new
11
+ end
12
+
13
+ def create_session(session_id = nil, attributes = {})
14
+ session_id ||= SecureRandom.hex(6)
15
+
16
+ session_data = {
17
+ id: session_id,
18
+ status: "pre_initialize",
19
+ initialized: false,
20
+ role: "server",
21
+ messages_count: 0,
22
+ sse_event_counter: 0,
23
+ created_at: Time.current,
24
+ updated_at: Time.current
25
+ }.merge(attributes)
26
+
27
+ session = MemorySession.new(session_data, self)
28
+
29
+ # Initialize server info and capabilities if server role
30
+ if session.role == "server"
31
+ session.server_info = {
32
+ name: ActionMCP.configuration.name,
33
+ version: ActionMCP.configuration.version
34
+ }
35
+ session.server_capabilities = ActionMCP.configuration.capabilities
36
+
37
+ # Initialize registries
38
+ session.tool_registry = ActionMCP.configuration.filtered_tools.map(&:name)
39
+ session.prompt_registry = ActionMCP.configuration.filtered_prompts.map(&:name)
40
+ session.resource_registry = ActionMCP.configuration.filtered_resources.map(&:name)
41
+ end
42
+
43
+ @sessions[session_id] = session
44
+ session
45
+ end
46
+
47
+ def load_session(session_id)
48
+ session = @sessions[session_id]
49
+ if session
50
+ session.instance_variable_set(:@new_record, false)
51
+ end
52
+ session
53
+ end
54
+
55
+ def save_session(session)
56
+ @sessions[session.id] = session
57
+ end
58
+
59
+ def delete_session(session_id)
60
+ @sessions.delete(session_id)
61
+ end
62
+
63
+ def session_exists?(session_id)
64
+ @sessions.key?(session_id)
65
+ end
66
+
67
+ def find_sessions(criteria = {})
68
+ sessions = @sessions.values
69
+
70
+ # Filter by status
71
+ if criteria[:status]
72
+ sessions = sessions.select { |s| s.status == criteria[:status] }
73
+ end
74
+
75
+ # Filter by role
76
+ if criteria[:role]
77
+ sessions = sessions.select { |s| s.role == criteria[:role] }
78
+ end
79
+
80
+ sessions
81
+ end
82
+
83
+ def cleanup_expired_sessions(older_than: 24.hours.ago)
84
+ expired_ids = @sessions.select do |_id, session|
85
+ session.updated_at < older_than
86
+ end.keys
87
+
88
+ expired_ids.each { |id| @sessions.delete(id) }
89
+ expired_ids.count
90
+ end
91
+
92
+ def clear_all
93
+ @sessions.clear
94
+ end
95
+
96
+ def session_count
97
+ @sessions.size
98
+ end
99
+ end
100
+ end
101
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "gem_version"
4
4
  module ActionMCP
5
- VERSION = "0.52.0"
5
+ VERSION = "0.52.2"
6
6
 
7
7
  class << self
8
8
  alias version gem_version
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.52.0
4
+ version: 0.52.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -137,6 +137,7 @@ files:
137
137
  - lib/action_mcp/callbacks.rb
138
138
  - lib/action_mcp/capability.rb
139
139
  - lib/action_mcp/client.rb
140
+ - lib/action_mcp/client/active_record_session_store.rb
140
141
  - lib/action_mcp/client/base.rb
141
142
  - lib/action_mcp/client/blueprint.rb
142
143
  - lib/action_mcp/client/catalog.rb
@@ -151,11 +152,14 @@ files:
151
152
  - lib/action_mcp/client/roots.rb
152
153
  - lib/action_mcp/client/server.rb
153
154
  - lib/action_mcp/client/session_store.rb
155
+ - lib/action_mcp/client/session_store_factory.rb
154
156
  - lib/action_mcp/client/sse_client.rb
155
157
  - lib/action_mcp/client/streamable_http_transport.rb
158
+ - lib/action_mcp/client/test_session_store.rb
156
159
  - lib/action_mcp/client/toolbox.rb
157
160
  - lib/action_mcp/client/tools.rb
158
161
  - lib/action_mcp/client/transport.rb
162
+ - lib/action_mcp/client/volatile_session_store.rb
159
163
  - lib/action_mcp/configuration.rb
160
164
  - lib/action_mcp/console_detector.rb
161
165
  - lib/action_mcp/content.rb
@@ -187,6 +191,7 @@ files:
187
191
  - lib/action_mcp/resource_template.rb
188
192
  - lib/action_mcp/resource_templates_registry.rb
189
193
  - lib/action_mcp/server.rb
194
+ - lib/action_mcp/server/active_record_session_store.rb
190
195
  - lib/action_mcp/server/base_messaging.rb
191
196
  - lib/action_mcp/server/capabilities.rb
192
197
  - lib/action_mcp/server/configuration.rb
@@ -196,6 +201,7 @@ files:
196
201
  - lib/action_mcp/server/handlers/resource_handler.rb
197
202
  - lib/action_mcp/server/handlers/tool_handler.rb
198
203
  - lib/action_mcp/server/json_rpc_handler.rb
204
+ - lib/action_mcp/server/memory_session.rb
199
205
  - lib/action_mcp/server/messaging.rb
200
206
  - lib/action_mcp/server/notifications.rb
201
207
  - lib/action_mcp/server/prompts.rb
@@ -206,10 +212,13 @@ files:
206
212
  - lib/action_mcp/server/sampling.rb
207
213
  - lib/action_mcp/server/sampling_request.rb
208
214
  - lib/action_mcp/server/session_store.rb
215
+ - lib/action_mcp/server/session_store_factory.rb
209
216
  - lib/action_mcp/server/simple_pub_sub.rb
210
217
  - lib/action_mcp/server/solid_cable_adapter.rb
218
+ - lib/action_mcp/server/test_session_store.rb
211
219
  - lib/action_mcp/server/tools.rb
212
220
  - lib/action_mcp/server/transport_handler.rb
221
+ - lib/action_mcp/server/volatile_session_store.rb
213
222
  - lib/action_mcp/sse_listener.rb
214
223
  - lib/action_mcp/string_array.rb
215
224
  - lib/action_mcp/tagged_stream_logging.rb