actionmcp 0.50.7 → 0.50.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17696f3f1e92327c6de4d8fa4ae5b6585bf8d9e173acb47beaf713dd9d1f8b07
4
- data.tar.gz: 536f24be37c5c2fbc4f440fbf1c977eadee964cd6717be5981d63cbd35081cb7
3
+ metadata.gz: d09ebd13460c234fc23c4c509ffec0b9cb48ea6607108217be7309aee8dbf617
4
+ data.tar.gz: b269226676ee4974b7757093e721cca942eb7acb60e47fa72b6999b780a7bf7f
5
5
  SHA512:
6
- metadata.gz: cf5c45e416e66526ee4550d611e0bbea71a3150b2f8f02af58899450308399c27e3f710348feed1d8478b27acc6701b1d01e9d6e6ab4e6f763f43ede2051d526
7
- data.tar.gz: 8a27fae5101d5deb20e5f3b3b7da0a2d3d5bdfdef04681737de63f985a34a7f8cb4e3f8a9f08ae2cf886662f7855c453b19325eb5ae3ce5ec9bca141cc5bcde7
6
+ metadata.gz: e6300709a72e3d8c54d23c4c2ba23bd751c2bac1b5c25f7b7ba5a8ff6d60f455571ef4b1a8e212b2cb22d05b3e06b803926d5b39d2daf9446b38d6296ea3277a
7
+ data.tar.gz: a2385c3f8910c27b8b36ce7d8cef73d6cf6db18922c0b6d9a7f51abd43d5a06a35f04e715cbfe32b1dbf7c8793a72f4d94b0142553d94e4360b9b1e5825164f9
@@ -160,8 +160,10 @@ module ActionMCP
160
160
 
161
161
  transport_handler = Server::TransportHandler.new(session)
162
162
  json_rpc_handler = Server::JsonRpcHandler.new(transport_handler)
163
- handler_results = json_rpc_handler.call(jsonrpc_params)
164
- process_handler_results(handler_results, session, session_initially_missing, is_initialize_request)
163
+
164
+ result = json_rpc_handler.call(jsonrpc_params)
165
+
166
+ process_handler_results(result, session, session_initially_missing, is_initialize_request)
165
167
  rescue ActionController::Live::ClientDisconnected, IOError => e
166
168
  Rails.logger.debug "Unified SSE (POST): Client disconnected during response: #{e.message}"
167
169
  begin
@@ -257,45 +259,24 @@ module ActionMCP
257
259
  end
258
260
 
259
261
  # Processes the results from the JsonRpcHandler.
260
- def process_handler_results(results, session, session_initially_missing, is_initialize_request)
261
- results ||= {}
262
- is_notification = jsonrpc_params.is_a?(JSON_RPC::Notification)
263
- request_id = nil
264
- if results.is_a?(Hash)
265
- request_id = results[:request_id] || results[:id]
266
- request_id ||= results[:payload][:id] if results[:payload].is_a?(Hash) && results[:payload][:id]
262
+ def process_handler_results(result, session, session_initially_missing, is_initialize_request)
263
+ # Handle empty result (notifications)
264
+ if result.nil?
265
+ return head :accepted
267
266
  end
268
- result_type = results[:type]
269
- result_payload = results[:payload] || {}
270
- result_payload[:id] = request_id if result_payload.is_a?(Hash) && request_id && !result_payload.key?(:id)
271
-
272
- case result_type
273
- when :error
274
- error_payload = result_payload
275
- error_payload[:id] = request_id if error_payload.is_a?(Hash) && !error_payload.key?(:id) && request_id
276
- render json: error_payload, status: results.fetch(:status, :bad_request)
277
- when :notifications_only
278
- head :accepted
279
- when :responses
280
- server_preference = ActionMCP.configuration.post_response_preference
281
- use_sse = (server_preference == :sse)
282
- add_session_header = is_initialize_request && session_initially_missing && session.persisted?
283
- if use_sse
284
- render_sse_response(result_payload, session, add_session_header)
285
- else
286
- render_json_response(result_payload, session, add_session_header)
287
- end
267
+
268
+ # Convert to hash for rendering
269
+ payload = result.message_json
270
+
271
+ # Determine response format
272
+ server_preference = ActionMCP.configuration.post_response_preference
273
+ use_sse = (server_preference == :sse)
274
+ add_session_header = is_initialize_request && session_initially_missing && session.persisted?
275
+
276
+ if use_sse
277
+ render_sse_response(payload, session, add_session_header)
288
278
  else
289
- Rails.logger.error "Unknown handler result type: #{result_type.inspect}"
290
- if is_notification
291
- head :accepted
292
- else
293
- render json: {
294
- jsonrpc: "2.0",
295
- id: request_id,
296
- result: result_payload
297
- }, status: :ok
298
- end
279
+ render_json_response(payload, session, add_session_header)
299
280
  end
300
281
  end
301
282
 
@@ -332,7 +313,6 @@ module ActionMCP
332
313
  data = payload.is_a?(String) ? payload : MultiJson.dump(payload)
333
314
  sse_event = "id: #{event_id}\ndata: #{data}\n\n"
334
315
  sse.write(sse_event)
335
- return unless ActionMCP.configuration.enable_sse_resumability
336
316
 
337
317
  begin
338
318
  session.store_sse_event(event_id, payload, session.max_stored_sse_events)
@@ -343,8 +323,6 @@ module ActionMCP
343
323
 
344
324
  # Helper to clean up old SSE events for a session
345
325
  def cleanup_old_sse_events(session)
346
- return unless ActionMCP.configuration.enable_sse_resumability
347
-
348
326
  begin
349
327
  retention_period = session.sse_event_retention_period
350
328
  count = session.cleanup_old_sse_events(retention_period)
@@ -29,7 +29,6 @@ module ActionMCP
29
29
  # --- VibedIgnoreVersion Option ---
30
30
  :vibed_ignore_version,
31
31
  # --- SSE Resumability Options ---
32
- :enable_sse_resumability,
33
32
  :sse_event_retention_period,
34
33
  :max_stored_sse_events
35
34
 
@@ -46,7 +45,6 @@ module ActionMCP
46
45
  @vibed_ignore_version = false
47
46
 
48
47
  # Resumability defaults
49
- @enable_sse_resumability = true
50
48
  @sse_event_retention_period = 15.minutes
51
49
  @max_stored_sse_events = 100
52
50
  end
@@ -141,9 +139,6 @@ module ActionMCP
141
139
 
142
140
  capabilities[:resources] = { subscribe: @resources_subscribe } if filtered_resources.any?
143
141
 
144
- # Add resumability capability if enabled
145
- capabilities[:resumability] = { enabled: @enable_sse_resumability } if @enable_sse_resumability
146
-
147
142
  capabilities
148
143
  end
149
144
 
@@ -56,12 +56,8 @@ module ActionMCP
56
56
  case rpc_method
57
57
  when Methods::PING
58
58
  transport.send_pong(id)
59
- true
60
59
  when %r{^notifications/}
61
60
  process_notifications(rpc_method, params)
62
- true
63
- else
64
- false
65
61
  end
66
62
  end
67
63
 
@@ -15,48 +15,32 @@ module ActionMCP
15
15
  client_capabilities = params["capabilities"]
16
16
 
17
17
  unless client_protocol_version.is_a?(String) && client_protocol_version.present?
18
- send_jsonrpc_error(request_id, :invalid_params, "Missing or invalid 'protocolVersion'")
19
- return { type: :error, id: request_id,
20
- payload: { jsonrpc: "2.0", id: request_id, error: { code: -32_602, message: "Missing or invalid 'protocolVersion'" } } }
18
+ return send_jsonrpc_error(request_id, :invalid_params, "Missing or invalid 'protocolVersion'")
21
19
  end
22
- # Check if the protocol version is supported
23
20
  unless ActionMCP.configuration.vibed_ignore_version || ActionMCP::SUPPORTED_VERSIONS.include?(client_protocol_version)
24
21
  error_data = {
25
22
  supported: ActionMCP::SUPPORTED_VERSIONS,
26
23
  requested: client_protocol_version
27
24
  }
28
- send_jsonrpc_error(request_id, :invalid_params, "Unsupported protocol version", error_data)
29
- return { type: :error, id: request_id,
30
- payload: { jsonrpc: "2.0", id: request_id, error: { code: -32_602, message: "Unsupported protocol version", data: error_data } } }
25
+ return send_jsonrpc_error(request_id, :invalid_params, "Unsupported protocol version", error_data)
31
26
  end
32
27
 
33
28
  unless client_info.is_a?(Hash)
34
- send_jsonrpc_error(request_id, :invalid_params, "Missing or invalid 'clientInfo'")
35
- return { type: :error, id: request_id,
36
- payload: { jsonrpc: "2.0", id: request_id, error: { code: -32_602, message: "Missing or invalid 'clientInfo'" } } }
29
+ return send_jsonrpc_error(request_id, :invalid_params, "Missing or invalid 'clientInfo'")
37
30
  end
38
31
  unless client_capabilities.is_a?(Hash)
39
- send_jsonrpc_error(request_id, :invalid_params, "Missing or invalid 'capabilities'")
40
- return { type: :error, id: request_id,
41
- payload: { jsonrpc: "2.0", id: request_id, error: { code: -32_602, message: "Missing or invalid 'capabilities'" } } }
32
+ return send_jsonrpc_error(request_id, :invalid_params, "Missing or invalid 'capabilities'")
42
33
  end
43
34
 
44
- # Store client information
45
35
  session.store_client_info(client_info)
46
36
  session.store_client_capabilities(client_capabilities)
47
37
  session.set_protocol_version(client_protocol_version)
48
38
 
49
- # Initialize the session
50
39
  unless session.initialize!
51
- send_jsonrpc_error(request_id, :internal_error, "Failed to initialize session")
52
- return { type: :error, id: request_id,
53
- payload: { jsonrpc: "2.0", id: request_id, error: { code: -32_603, message: "Failed to initialize session" } } }
40
+ return send_jsonrpc_error(request_id, :internal_error, "Failed to initialize session")
54
41
  end
55
42
 
56
- # Send the successful response with the correct protocol version
57
43
  capabilities_payload = session.server_capabilities_payload
58
- # If vibed_ignore_version is true, always use the latest supported version in the response
59
- # Otherwise, use the client's requested version
60
44
  capabilities_payload[:protocolVersion] = if ActionMCP.configuration.vibed_ignore_version
61
45
  PROTOCOL_VERSION
62
46
  else
@@ -64,7 +48,6 @@ module ActionMCP
64
48
  end
65
49
 
66
50
  send_jsonrpc_response(request_id, result: capabilities_payload)
67
- { type: :responses, id: request_id, payload: { jsonrpc: "2.0", id: request_id, result: capabilities_payload } }
68
51
  end
69
52
  end
70
53
  end
@@ -12,19 +12,10 @@ module ActionMCP
12
12
  when Symbol
13
13
  JSON_RPC::JsonRpcError.new(error_or_symbol, message: message, data: data)
14
14
  else
15
- # If it's already an error hash
16
15
  error_or_symbol
17
16
  end
18
17
 
19
- {
20
- type: :error,
21
- request_id: id,
22
- payload: {
23
- jsonrpc: "2.0",
24
- id: id,
25
- error: json_rpc_error.to_h
26
- }
27
- }
18
+ JSON_RPC::Response.new(id: id, error: json_rpc_error)
28
19
  end
29
20
 
30
21
  # Helper method to create error response from any exception
@@ -32,14 +32,11 @@ module ActionMCP
32
32
  def handle_prompts_get(id, params)
33
33
  name = extract_name(params)
34
34
  arguments = extract_arguments(params)
35
-
36
- message = transport.send_prompts_get(id, name, arguments)
37
- extract_message_payload(message, id)
35
+ transport.send_prompts_get(id, name, arguments)
38
36
  end
39
37
 
40
38
  def handle_prompts_list(id, _params)
41
- message = transport.send_prompts_list(id)
42
- extract_message_payload(message, id)
39
+ transport.send_prompts_list(id)
43
40
  end
44
41
 
45
42
  def extract_name(params)
@@ -33,20 +33,16 @@ module ActionMCP
33
33
  end
34
34
 
35
35
  def handle_resources_list(id, _params)
36
- message = transport.send_resources_list(id)
37
- extract_message_payload(message, id)
36
+ transport.send_resources_list(id)
38
37
  end
39
38
 
40
39
  def handle_resources_templates_list(id, _params)
41
- message = transport.send_resource_templates_list(id)
42
- extract_message_payload(message, id)
40
+ transport.send_resource_templates_list(id)
43
41
  end
44
42
 
45
43
  def handle_resources_read(id, params)
46
44
  validate_params_present(params, "Resource URI is required")
47
-
48
- message = transport.send_resource_read(id, params)
49
- extract_message_payload(message, id)
45
+ transport.send_resource_read(id, params)
50
46
  end
51
47
 
52
48
  def handle_resources_subscribe(id, params)
@@ -30,16 +30,13 @@ module ActionMCP
30
30
  end
31
31
 
32
32
  def handle_tools_list(id, params)
33
- message = transport.send_tools_list(id, params)
34
- extract_message_payload(message, id)
33
+ transport.send_tools_list(id, params)
35
34
  end
36
35
 
37
36
  def handle_tools_call(id, params)
38
37
  name = validate_required_param(params, "name", "Tool name is required")
39
38
  arguments = extract_arguments(params)
40
-
41
- message = transport.send_tools_call(id, name, arguments)
42
- extract_message_payload(message, id)
39
+ transport.send_tools_call(id, name, arguments)
43
40
  end
44
41
 
45
42
  def extract_arguments(params)
@@ -32,10 +32,8 @@ module ActionMCP
32
32
  params = request.params
33
33
 
34
34
  with_error_handling(id) do
35
- # Try to handle common methods first (like ping)
36
- return if handle_common_methods(rpc_method, id, params)
37
-
38
- # Route to appropriate handler
35
+ common_method = handle_common_methods(rpc_method, id, params)
36
+ return common_method if common_method
39
37
  route_to_handler(rpc_method, id, params)
40
38
  end
41
39
  end
@@ -58,8 +56,7 @@ module ActionMCP
58
56
  end
59
57
 
60
58
  def handle_initialize(id, params)
61
- message = transport.send_capabilities(id, params)
62
- extract_message_payload(message, id)
59
+ transport.send_capabilities(id, params)
63
60
  end
64
61
 
65
62
  def handle_notification(notification)
@@ -67,29 +64,17 @@ module ActionMCP
67
64
  params = notification.params || {}
68
65
 
69
66
  process_notifications(method_name, params)
70
- { type: :notifications_only }
67
+ nil
71
68
  end
72
69
 
73
70
  def handle_response(response)
74
71
  Rails.logger.debug("Received response: #{response.inspect}")
75
-
76
- {
77
- type: :responses,
78
- request_id: response.id,
79
- payload: build_response_payload(response)
80
- }
72
+ response
81
73
  end
82
74
 
83
- def process_completion_complete(id, params)
84
- params ||= {}
85
75
 
86
- result = transport.send_jsonrpc_response(id, result: build_completion_result)
87
-
88
- if result.is_a?(ActionMCP::Session::Message)
89
- extract_message_payload(result, id)
90
- else
91
- wrap_transport_result(result, id)
92
- end
76
+ def process_completion_complete(id, params)
77
+ transport.send_jsonrpc_response(id, result: build_completion_result)
93
78
  end
94
79
 
95
80
  def process_notifications(rpc_method, params)
@@ -101,30 +86,6 @@ module ActionMCP
101
86
  end
102
87
  end
103
88
 
104
- def extract_message_payload(message, id)
105
- if message.is_a?(ActionMCP::Session::Message)
106
- {
107
- type: :responses,
108
- request_id: id,
109
- payload: message.message_json
110
- }
111
- else
112
- message
113
- end
114
- end
115
-
116
- def wrap_transport_result(transport_result, id)
117
- if transport_result.is_a?(Hash) && transport_result[:type]
118
- transport_result
119
- else
120
- {
121
- type: :responses,
122
- request_id: id,
123
- payload: transport_result
124
- }
125
- end
126
- end
127
-
128
89
  def build_response_payload(response)
129
90
  {
130
91
  jsonrpc: "2.0",
@@ -56,22 +56,6 @@ module ActionMCP
56
56
  if raw_message.is_a?(String) && valid_json_format?(raw_message)
57
57
  message = MultiJson.load(raw_message)
58
58
  callback&.call(message)
59
- elsif raw_message.respond_to?(:message) && raw_message.message.is_a?(String) && valid_json_format?(raw_message.message)
60
- message = MultiJson.load(raw_message.message)
61
- callback&.call(message)
62
- elsif raw_message.respond_to?(:to_json)
63
- # Try to serialize the message object to JSON if it responds to to_json
64
- message_json = raw_message.to_json
65
- if valid_json_format?(message_json)
66
- message = MultiJson.load(message_json)
67
- callback&.call(message)
68
- else
69
- Rails.logger.warn "SSEListener: Message cannot be converted to valid JSON"
70
- end
71
- else
72
- # Log that we received an invalid message format
73
- display_message = raw_message.to_s[0..100]
74
- Rails.logger.warn "SSEListener: Received invalid JSON format: #{display_message}..."
75
59
  end
76
60
  rescue StandardError => e
77
61
  Rails.logger.error "SSEListener: Error processing message: #{e.message}"
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "gem_version"
4
4
  module ActionMCP
5
- VERSION = "0.50.7"
5
+ VERSION = "0.50.10"
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.50.7
4
+ version: 0.50.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih