actionmcp 0.50.5 → 0.50.7

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: 334dbf522501eda848a1ab5d66378792b71acfd4177366de980e868be405d4dc
4
- data.tar.gz: 1d29dd025817770f2389e99e9e11978e3ca813af5326d1c89ffef867267a82be
3
+ metadata.gz: 17696f3f1e92327c6de4d8fa4ae5b6585bf8d9e173acb47beaf713dd9d1f8b07
4
+ data.tar.gz: 536f24be37c5c2fbc4f440fbf1c977eadee964cd6717be5981d63cbd35081cb7
5
5
  SHA512:
6
- metadata.gz: 5c7ef8680677a244d909f482440aeed23033cfb9fbe4d109a14b474eefc4eec061436c22c39bd9b161f84c30cda9e707b9b79acf96bf54582e4fadef219ce5bd
7
- data.tar.gz: b8f01db29cac834c91d75353dbeaa9d96c2cc5646c04eb8d17f625b63432e1304d0ae85a7a41a9f892020ab1d64a6c2fafe2cc2d28e017bc3184886da677d9f6
6
+ metadata.gz: cf5c45e416e66526ee4550d611e0bbea71a3150b2f8f02af58899450308399c27e3f710348feed1d8478b27acc6701b1d01e9d6e6ab4e6f763f43ede2051d526
7
+ data.tar.gz: 8a27fae5101d5deb20e5f3b3b7da0a2d3d5bdfdef04681737de63f985a34a7f8cb4e3f8a9f08ae2cf886662f7855c453b19325eb5ae3ce5ec9bca141cc5bcde7
@@ -137,9 +137,7 @@ module ActionMCP
137
137
  # Handles POST requests containing client JSON-RPC messages according to 2025-03-26 spec.
138
138
  # @route POST /mcp
139
139
  def create
140
- unless post_accept_headers_valid?
141
- return render_not_acceptable(post_accept_headers_error_message)
142
- end
140
+ return render_not_acceptable(post_accept_headers_error_message) unless post_accept_headers_valid?
143
141
 
144
142
  is_initialize_request = check_if_initialize_request(jsonrpc_params)
145
143
  session_initially_missing = extract_session_id.nil?
@@ -209,7 +207,9 @@ module ActionMCP
209
207
  session = Session.find_by(id: session_id)
210
208
  if session
211
209
  if ActionMCP.configuration.vibed_ignore_version
212
- session.update!(protocol_version: self.class::REQUIRED_PROTOCOL_VERSION) if session.protocol_version != self.class::REQUIRED_PROTOCOL_VERSION
210
+ if session.protocol_version != self.class::REQUIRED_PROTOCOL_VERSION
211
+ session.update!(protocol_version: self.class::REQUIRED_PROTOCOL_VERSION)
212
+ end
213
213
  elsif session.protocol_version != self.class::REQUIRED_PROTOCOL_VERSION
214
214
  session.update!(protocol_version: self.class::REQUIRED_PROTOCOL_VERSION)
215
215
  end
@@ -252,6 +252,7 @@ module ActionMCP
252
252
  # Checks if the parsed body represents an 'initialize' request.
253
253
  def check_if_initialize_request(payload)
254
254
  return false unless payload.is_a?(JSON_RPC::Request) && !jsonrpc_params_batch?
255
+
255
256
  payload.method == "initialize"
256
257
  end
257
258
 
@@ -332,6 +333,7 @@ module ActionMCP
332
333
  sse_event = "id: #{event_id}\ndata: #{data}\n\n"
333
334
  sse.write(sse_event)
334
335
  return unless ActionMCP.configuration.enable_sse_resumability
336
+
335
337
  begin
336
338
  session.store_sse_event(event_id, payload, session.max_stored_sse_events)
337
339
  rescue StandardError => e
@@ -342,6 +344,7 @@ module ActionMCP
342
344
  # Helper to clean up old SSE events for a session
343
345
  def cleanup_old_sse_events(session)
344
346
  return unless ActionMCP.configuration.enable_sse_resumability
347
+
345
348
  begin
346
349
  retention_period = session.sse_event_retention_period
347
350
  count = session.cleanup_old_sse_events(retention_period)
@@ -64,7 +64,9 @@ module ActionMCP
64
64
  before_create :set_server_info, if: -> { role == "server" }
65
65
  before_create :set_server_capabilities, if: -> { role == "server" }
66
66
 
67
- validates :protocol_version, inclusion: { in: SUPPORTED_VERSIONS }, allow_nil: true, unless: -> { ActionMCP.configuration.vibed_ignore_version }
67
+ validates :protocol_version, inclusion: { in: SUPPORTED_VERSIONS }, allow_nil: true, unless: lambda {
68
+ ActionMCP.configuration.vibed_ignore_version
69
+ }
68
70
 
69
71
  def close!
70
72
  dummy_callback = ->(*) { } # this callback seem broken
@@ -58,6 +58,7 @@ module ActionMCP
58
58
  # @return [Array<Object>] The next page of items, or empty array if no more pages
59
59
  def next_page(limit: nil)
60
60
  return [] unless has_more_pages?
61
+
61
62
  page(cursor: @next_cursor, limit: limit)
62
63
  end
63
64
 
@@ -66,7 +67,7 @@ module ActionMCP
66
67
  # @param limit [Integer, nil] Optional limit for page size
67
68
  # @yield [page] Block to process each page
68
69
  # @yieldparam page [Array<Object>] A page of items
69
- def each_page(limit: nil, &block)
70
+ def each_page(limit: nil)
70
71
  return unless block_given?
71
72
 
72
73
  current_page = page(limit: limit)
@@ -125,12 +126,10 @@ module ActionMCP
125
126
  params[:cursor] = cursor if cursor
126
127
  params[:limit] = limit if limit
127
128
 
128
- request_id = client.send(@load_method, params)
129
+ client.send(@load_method, params)
129
130
 
130
131
  start_time = Time.now
131
- while !@loaded && (Time.now - start_time) < timeout
132
- sleep(0.1)
133
- end
132
+ sleep(0.1) while !@loaded && (Time.now - start_time) < timeout
134
133
 
135
134
  # Update @loaded status even if we timed out
136
135
  @loaded = true
@@ -23,8 +23,7 @@ module ActionMCP
23
23
  #
24
24
  # @return [Hash] The hash representation of the image content.
25
25
  def to_h
26
- h = super.merge(data: @data, mimeType: @mime_type)
27
- h
26
+ super.merge(data: @data, mimeType: @mime_type)
28
27
  end
29
28
  end
30
29
  end
@@ -2,6 +2,34 @@
2
2
 
3
3
  module ActionMCP
4
4
  class JsonRpcHandlerBase
5
+ module Methods
6
+ # Common methods
7
+ PING = "ping"
8
+
9
+ # Server methods
10
+ INITIALIZE = "initialize"
11
+ COMPLETION_COMPLETE = "completion/complete"
12
+
13
+ # Resource methods
14
+ RESOURCES_LIST = "resources/list"
15
+ RESOURCES_TEMPLATES_LIST = "resources/templates/list"
16
+ RESOURCES_READ = "resources/read"
17
+ RESOURCES_SUBSCRIBE = "resources/subscribe"
18
+ RESOURCES_UNSUBSCRIBE = "resources/unsubscribe"
19
+
20
+ # Prompt methods
21
+ PROMPTS_GET = "prompts/get"
22
+ PROMPTS_LIST = "prompts/list"
23
+
24
+ # Tool methods
25
+ TOOLS_LIST = "tools/list"
26
+ TOOLS_CALL = "tools/call"
27
+
28
+ # Notification methods
29
+ NOTIFICATIONS_INITIALIZED = "notifications/initialized"
30
+ NOTIFICATIONS_CANCELLED = "notifications/cancelled"
31
+ end
32
+
5
33
  delegate :initialize!, :initialized?, to: :transport
6
34
  delegate :write, :read, to: :transport
7
35
  attr_reader :transport
@@ -11,38 +39,14 @@ module ActionMCP
11
39
  @transport = transport
12
40
  end
13
41
 
14
- # Process a single line of input.
15
- # @param line [String, Hash]
16
- def call(line)
17
- request = if line.is_a?(String)
18
- line.strip!
19
- return if line.empty?
20
-
21
- begin
22
- MultiJson.load(line)
23
- rescue MultiJson::ParseError => e
24
- Rails.logger.error("Failed to parse JSON: #{e.message}")
25
- return
26
- end
27
- else
28
- line
29
- end
30
- process_request(request)
42
+ # Process a request object.
43
+ # @param request [JSON_RPC::Request, JSON_RPC::Notification, JSON_RPC::Response]
44
+ def call(request)
45
+ raise NotImplementedError, "Subclasses must implement call"
31
46
  end
32
47
 
33
48
  protected
34
49
 
35
- # Validate if the request follows JSON-RPC 2.0 specification
36
- # @param request [Hash]
37
- # @return [Boolean]
38
- def valid_request?(request)
39
- if request["jsonrpc"] != "2.0"
40
- puts "Invalid request: #{request}"
41
- return false
42
- end
43
- true
44
- end
45
-
46
50
  # Handle common methods for both client and server
47
51
  # @param rpc_method [String]
48
52
  # @param id [String, Integer]
@@ -50,11 +54,10 @@ module ActionMCP
50
54
  # @return [Boolean] true if handled, false otherwise
51
55
  def handle_common_methods(rpc_method, id, params)
52
56
  case rpc_method
53
- when "ping"
57
+ when Methods::PING
54
58
  transport.send_pong(id)
55
59
  true
56
60
  when %r{^notifications/}
57
- puts "\e[31mProcessing notifications\e[0m"
58
61
  process_notifications(rpc_method, params)
59
62
  true
60
63
  else
@@ -62,47 +65,22 @@ module ActionMCP
62
65
  end
63
66
  end
64
67
 
65
- # Method to be overridden by subclasses to handle specific RPC methods
66
- # @param rpc_method [String]
67
- # @param id [String, Integer]
68
- # @param params [Hash]
69
- def handle_method(rpc_method, id, params)
70
- raise NotImplementedError, "Subclasses must implement handle_method"
71
- end
72
-
73
- private
74
-
75
- # @param request [Hash]
76
- def process_request(request)
77
- return unless valid_request?(request)
78
-
79
- request = request.with_indifferent_access
80
-
81
- read(request)
82
- id = request["id"]
83
-
84
- unless (rpc_method = request["method"])
85
- # this is a response or a bobo
86
- return process_response(id, request["result"]) if request["result"]
87
- return process_error(id, request["error"]) if request["error"]
88
- end
89
-
90
- params = request["params"]
91
- # Try to handle common methods first
92
- return if handle_common_methods(rpc_method, id, params)
93
-
94
- # Delegate to subclass-specific handling
95
- handle_method(rpc_method, id, params)
96
- end
97
-
68
+ # Process notification methods
98
69
  def process_notifications(rpc_method, params)
99
70
  case rpc_method
100
- when "notifications/cancelled" # [BOTH] Request cancellation
101
- puts "\e[31m Request #{params['requestId']} cancelled: #{params['reason']}\e[0m"
102
- # we don't need to do anything here
71
+ when Methods::NOTIFICATIONS_CANCELLED
72
+ handle_cancelled_notification(params)
103
73
  else
104
74
  Rails.logger.warn("Unknown notifications method: #{rpc_method}")
105
75
  end
106
76
  end
77
+
78
+ private
79
+
80
+ # Handle cancelled notification
81
+ def handle_cancelled_notification(params)
82
+ Rails.logger.warn "\e[31m Request #{params['requestId']} cancelled: #{params['reason']}\e[0m"
83
+ # we don't need to do anything here
84
+ end
107
85
  end
108
86
  end
@@ -57,10 +57,10 @@ module ActionMCP
57
57
  capabilities_payload = session.server_capabilities_payload
58
58
  # If vibed_ignore_version is true, always use the latest supported version in the response
59
59
  # Otherwise, use the client's requested version
60
- if ActionMCP.configuration.vibed_ignore_version
61
- capabilities_payload[:protocolVersion] = PROTOCOL_VERSION
60
+ capabilities_payload[:protocolVersion] = if ActionMCP.configuration.vibed_ignore_version
61
+ PROTOCOL_VERSION
62
62
  else
63
- capabilities_payload[:protocolVersion] = client_protocol_version
63
+ client_protocol_version
64
64
  end
65
65
 
66
66
  send_jsonrpc_response(request_id, result: capabilities_payload)
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Server
5
+ module ErrorAware
6
+ private
7
+
8
+ # Validate required parameter and raise error if missing
9
+ def validate_required_param(params, key, error_message)
10
+ value = params[key] || params[key.to_sym]
11
+ raise JSON_RPC::JsonRpcError.new(:invalid_params, message: error_message) if value.nil?
12
+
13
+ value
14
+ end
15
+
16
+ # Validate params is not nil or empty
17
+ def validate_params_present(params, error_message)
18
+ raise JSON_RPC::JsonRpcError.new(:invalid_params, message: error_message) if params.nil? || params.empty?
19
+
20
+ params
21
+ end
22
+
23
+ # Safe execution with JSON-RPC error handling
24
+ def with_error_handling(request_id)
25
+ yield
26
+ rescue JSON_RPC::JsonRpcError => e
27
+ error_response(request_id, e)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Server
5
+ module ErrorHandling
6
+ private
7
+
8
+ def error_response(id, error_or_symbol, message = nil, data = nil)
9
+ json_rpc_error = case error_or_symbol
10
+ when JSON_RPC::JsonRpcError
11
+ error_or_symbol
12
+ when Symbol
13
+ JSON_RPC::JsonRpcError.new(error_or_symbol, message: message, data: data)
14
+ else
15
+ # If it's already an error hash
16
+ error_or_symbol
17
+ end
18
+
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
+ }
28
+ end
29
+
30
+ # Helper method to create error response from any exception
31
+ def error_response_from_exception(id, exception)
32
+ if exception.is_a?(JSON_RPC::JsonRpcError)
33
+ error_response(id, exception)
34
+ else
35
+ error_response(id, :internal_error, exception.message)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Server
5
+ module Handlers
6
+ module PromptHandler
7
+ include ErrorAware
8
+
9
+ def process_prompts(rpc_method, id, params)
10
+ params ||= {}
11
+
12
+ with_error_handling(id) do
13
+ handler = prompt_method_handlers[rpc_method]
14
+ if handler
15
+ send(handler, id, params)
16
+ else
17
+ Rails.logger.warn("Unknown prompts method: #{rpc_method}")
18
+ raise JSON_RPC::JsonRpcError.new(:method_not_found, message: "Unknown prompts method: #{rpc_method}")
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def prompt_method_handlers
26
+ {
27
+ JsonRpcHandlerBase::Methods::PROMPTS_GET => :handle_prompts_get,
28
+ JsonRpcHandlerBase::Methods::PROMPTS_LIST => :handle_prompts_list
29
+ }
30
+ end
31
+
32
+ def handle_prompts_get(id, params)
33
+ name = extract_name(params)
34
+ arguments = extract_arguments(params)
35
+
36
+ message = transport.send_prompts_get(id, name, arguments)
37
+ extract_message_payload(message, id)
38
+ end
39
+
40
+ def handle_prompts_list(id, _params)
41
+ message = transport.send_prompts_list(id)
42
+ extract_message_payload(message, id)
43
+ end
44
+
45
+ def extract_name(params)
46
+ params["name"] || params[:name]
47
+ end
48
+
49
+ def extract_arguments(params)
50
+ params["arguments"] || params[:arguments] || {}
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Server
5
+ module Handlers
6
+ module ResourceHandler
7
+ include ErrorAware
8
+
9
+ def process_resources(rpc_method, id, params)
10
+ params ||= {}
11
+
12
+ with_error_handling(id) do
13
+ handler = resource_method_handlers[rpc_method]
14
+ if handler
15
+ send(handler, id, params)
16
+ else
17
+ Rails.logger.warn("Unknown resources method: #{rpc_method}")
18
+ raise JSON_RPC::JsonRpcError.new(:method_not_found, message: "Unknown resources method: #{rpc_method}")
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def resource_method_handlers
26
+ {
27
+ JsonRpcHandlerBase::Methods::RESOURCES_LIST => :handle_resources_list,
28
+ JsonRpcHandlerBase::Methods::RESOURCES_TEMPLATES_LIST => :handle_resources_templates_list,
29
+ JsonRpcHandlerBase::Methods::RESOURCES_READ => :handle_resources_read,
30
+ JsonRpcHandlerBase::Methods::RESOURCES_SUBSCRIBE => :handle_resources_subscribe,
31
+ JsonRpcHandlerBase::Methods::RESOURCES_UNSUBSCRIBE => :handle_resources_unsubscribe
32
+ }
33
+ end
34
+
35
+ def handle_resources_list(id, _params)
36
+ message = transport.send_resources_list(id)
37
+ extract_message_payload(message, id)
38
+ end
39
+
40
+ def handle_resources_templates_list(id, _params)
41
+ message = transport.send_resource_templates_list(id)
42
+ extract_message_payload(message, id)
43
+ end
44
+
45
+ def handle_resources_read(id, params)
46
+ validate_params_present(params, "Resource URI is required")
47
+
48
+ message = transport.send_resource_read(id, params)
49
+ extract_message_payload(message, id)
50
+ end
51
+
52
+ def handle_resources_subscribe(id, params)
53
+ uri = validate_required_param(params, "uri", "Resource URI is required")
54
+
55
+ message = transport.send_resource_subscribe(id, uri)
56
+ extract_message_payload(message, id)
57
+ end
58
+
59
+ def handle_resources_unsubscribe(id, params)
60
+ uri = validate_required_param(params, "uri", "Resource URI is required")
61
+
62
+ message = transport.send_resource_unsubscribe(id, uri)
63
+ extract_message_payload(message, id)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Server
5
+ module Handlers
6
+ module ToolHandler
7
+ include ErrorAware
8
+
9
+ def process_tools(rpc_method, id, params)
10
+ params ||= {}
11
+
12
+ with_error_handling(id) do
13
+ handler = tool_method_handlers[rpc_method]
14
+ if handler
15
+ send(handler, id, params)
16
+ else
17
+ Rails.logger.warn("Unknown tools method: #{rpc_method}")
18
+ raise JSON_RPC::JsonRpcError.new(:method_not_found, message: "Unknown tools method: #{rpc_method}")
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def tool_method_handlers
26
+ {
27
+ JsonRpcHandlerBase::Methods::TOOLS_LIST => :handle_tools_list,
28
+ JsonRpcHandlerBase::Methods::TOOLS_CALL => :handle_tools_call
29
+ }
30
+ end
31
+
32
+ def handle_tools_list(id, params)
33
+ message = transport.send_tools_list(id, params)
34
+ extract_message_payload(message, id)
35
+ end
36
+
37
+ def handle_tools_call(id, params)
38
+ name = validate_required_param(params, "name", "Tool name is required")
39
+ arguments = extract_arguments(params)
40
+
41
+ message = transport.send_tools_call(id, name, arguments)
42
+ extract_message_payload(message, id)
43
+ end
44
+
45
+ def extract_arguments(params)
46
+ params["arguments"] || params[:arguments] || {}
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -3,10 +3,17 @@
3
3
  module ActionMCP
4
4
  module Server
5
5
  class JsonRpcHandler < JsonRpcHandlerBase
6
+ include Handlers::ResourceHandler
7
+ include Handlers::ToolHandler
8
+ include Handlers::PromptHandler
9
+ include ErrorHandling
10
+ include ErrorAware
11
+
6
12
  # Handle server-specific methods
7
13
  # @param request [JSON_RPC::Request, JSON_RPC::Notification, JSON_RPC::Response]
8
14
  def call(request)
9
15
  read(request.to_h)
16
+
10
17
  case request
11
18
  when JSON_RPC::Request
12
19
  handle_request(request)
@@ -20,134 +27,63 @@ module ActionMCP
20
27
  private
21
28
 
22
29
  def handle_request(request)
23
- @current_request_id = id = request.id
30
+ id = request.id
24
31
  rpc_method = request.method
25
32
  params = request.params
26
33
 
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
39
+ route_to_handler(rpc_method, id, params)
40
+ end
41
+ end
42
+
43
+ def route_to_handler(rpc_method, id, params)
27
44
  case rpc_method
28
- when "initialize"
29
- message = transport.send_capabilities(id, params)
30
- extract_message_payload(message, id)
45
+ when Methods::INITIALIZE
46
+ handle_initialize(id, params)
31
47
  when %r{^prompts/}
32
48
  process_prompts(rpc_method, id, params)
33
49
  when %r{^resources/}
34
50
  process_resources(rpc_method, id, params)
35
51
  when %r{^tools/}
36
52
  process_tools(rpc_method, id, params)
37
- when "completion/complete"
53
+ when Methods::COMPLETION_COMPLETE
38
54
  process_completion_complete(id, params)
39
55
  else
40
- error_response(id, :method_not_found, "Method not found: #{rpc_method}")
56
+ raise JSON_RPC::JsonRpcError.new(:method_not_found, message: "Method not found: #{rpc_method}")
41
57
  end
42
58
  end
43
59
 
60
+ def handle_initialize(id, params)
61
+ message = transport.send_capabilities(id, params)
62
+ extract_message_payload(message, id)
63
+ end
64
+
44
65
  def handle_notification(notification)
45
- @current_request_id = nil
46
-
47
- begin
48
- method_name = notification.method.to_s
49
- params = notification.params || {}
50
-
51
- process_notifications(method_name, params)
52
- { type: :notifications_only }
53
- rescue StandardError => e
54
- Rails.logger.error("Error handling notification #{notification.method}: #{e.message}")
55
- Rails.logger.error(e.backtrace.join("\n"))
56
- { type: :notifications_only }
57
- end
66
+ method_name = notification.method.to_s
67
+ params = notification.params || {}
68
+
69
+ process_notifications(method_name, params)
70
+ { type: :notifications_only }
58
71
  end
59
72
 
60
73
  def handle_response(response)
61
74
  Rails.logger.debug("Received response: #{response.inspect}")
75
+
62
76
  {
63
77
  type: :responses,
64
78
  request_id: response.id,
65
- payload: {
66
- jsonrpc: "2.0",
67
- id: response.id,
68
- result: response.result
69
- }
79
+ payload: build_response_payload(response)
70
80
  }
71
81
  end
72
82
 
73
- def process_prompts(rpc_method, id, params)
74
- params ||= {}
75
-
76
- case rpc_method
77
- when "prompts/get"
78
- name = params["name"] || params[:name]
79
- arguments = params["arguments"] || params[:arguments] || {}
80
- message = transport.send_prompts_get(id, name, arguments)
81
- extract_message_payload(message, id)
82
- when "prompts/list"
83
- message = transport.send_prompts_list(id)
84
- extract_message_payload(message, id)
85
- else
86
- Rails.logger.warn("Unknown prompts method: #{rpc_method}")
87
- error_response(id, :method_not_found, "Unknown prompts method: #{rpc_method}")
88
- end
89
- end
90
-
91
- def process_tools(rpc_method, id, params)
92
- params ||= {}
93
-
94
- case rpc_method
95
- when "tools/list"
96
- message = transport.send_tools_list(id, params)
97
- extract_message_payload(message, id)
98
- when "tools/call"
99
- name = params["name"] || params[:name]
100
- arguments = params["arguments"] || params[:arguments] || {}
101
-
102
- return error_response(id, :invalid_params, "Tool name is required") if name.nil?
103
-
104
- message = transport.send_tools_call(id, name, arguments)
105
- extract_message_payload(message, id)
106
- else
107
- Rails.logger.warn("Unknown tools method: #{rpc_method}")
108
- error_response(id, :method_not_found, "Unknown tools method: #{rpc_method}")
109
- end
110
- end
111
-
112
- def process_resources(rpc_method, id, params)
113
- params ||= {}
114
-
115
- case rpc_method
116
- when "resources/list"
117
- message = transport.send_resources_list(id)
118
- extract_message_payload(message, id)
119
- when "resources/templates/list"
120
- message = transport.send_resource_templates_list(id)
121
- extract_message_payload(message, id)
122
- when "resources/read"
123
- return error_response(id, :invalid_params, "Resource URI is required") if params.nil? || params.empty?
124
-
125
- message = transport.send_resource_read(id, params)
126
- extract_message_payload(message, id)
127
- when "resources/subscribe"
128
- uri = params["uri"] || params[:uri]
129
- return error_response(id, :invalid_params, "Resource URI is required") if uri.nil?
130
-
131
- message = transport.send_resource_subscribe(id, uri)
132
- extract_message_payload(message, id)
133
- when "resources/unsubscribe"
134
- uri = params["uri"] || params[:uri]
135
- return error_response(id, :invalid_params, "Resource URI is required") if uri.nil?
136
-
137
- message = transport.send_resource_unsubscribe(id, uri)
138
- extract_message_payload(message, id)
139
- else
140
- Rails.logger.warn("Unknown resources method: #{rpc_method}")
141
- error_response(id, :method_not_found, "Unknown resources method: #{rpc_method}")
142
- end
143
- end
144
-
145
83
  def process_completion_complete(id, params)
146
84
  params ||= {}
147
85
 
148
- result = transport.send_jsonrpc_response(id, result: {
149
- completion: { values: [], total: 0, hasMore: false }
150
- })
86
+ result = transport.send_jsonrpc_response(id, result: build_completion_result)
151
87
 
152
88
  if result.is_a?(ActionMCP::Session::Message)
153
89
  extract_message_payload(result, id)
@@ -158,8 +94,7 @@ module ActionMCP
158
94
 
159
95
  def process_notifications(rpc_method, params)
160
96
  case rpc_method
161
- when "notifications/initialized"
162
- Rails.logger.info "Client notified initialization complete"
97
+ when Methods::NOTIFICATIONS_INITIALIZED
163
98
  transport.initialize!
164
99
  else
165
100
  super
@@ -174,7 +109,7 @@ module ActionMCP
174
109
  payload: message.message_json
175
110
  }
176
111
  else
177
- message
112
+ message
178
113
  end
179
114
  end
180
115
 
@@ -190,23 +125,17 @@ module ActionMCP
190
125
  end
191
126
  end
192
127
 
193
- def error_response(id, code, message)
194
- error_code = case code
195
- when :method_not_found then -32_601
196
- when :invalid_params then -32_602
197
- when :internal_error then -32_603
198
- else -32_000
199
- end
128
+ def build_response_payload(response)
129
+ {
130
+ jsonrpc: "2.0",
131
+ id: response.id,
132
+ result: response.result
133
+ }
134
+ end
200
135
 
136
+ def build_completion_result
201
137
  {
202
- type: :error,
203
- request_id: id,
204
- payload: {
205
- jsonrpc: "2.0",
206
- id: id,
207
- error: { code: error_code, message: message }
208
- },
209
- status: :bad_request
138
+ completion: { values: [], total: 0, hasMore: false }
210
139
  }
211
140
  end
212
141
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "gem_version"
4
4
  module ActionMCP
5
- VERSION = "0.50.5"
5
+ VERSION = "0.50.7"
6
6
 
7
7
  class << self
8
8
  alias version gem_version
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionmcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.50.5
4
+ version: 0.50.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-05-14 00:00:00.000000000 Z
11
+ date: 2025-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -169,6 +169,11 @@ files:
169
169
  - lib/action_mcp/server.rb
170
170
  - lib/action_mcp/server/capabilities.rb
171
171
  - lib/action_mcp/server/configuration.rb
172
+ - lib/action_mcp/server/error_aware.rb
173
+ - lib/action_mcp/server/error_handling.rb
174
+ - lib/action_mcp/server/handlers/prompt_handler.rb
175
+ - lib/action_mcp/server/handlers/resource_handler.rb
176
+ - lib/action_mcp/server/handlers/tool_handler.rb
172
177
  - lib/action_mcp/server/json_rpc_handler.rb
173
178
  - lib/action_mcp/server/messaging.rb
174
179
  - lib/action_mcp/server/notifications.rb