actionmcp 0.51.0 → 0.52.1

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +192 -0
  3. data/app/controllers/action_mcp/application_controller.rb +12 -6
  4. data/lib/action_mcp/client/active_record_session_store.rb +57 -0
  5. data/lib/action_mcp/client/session_store.rb +2 -103
  6. data/lib/action_mcp/client/session_store_factory.rb +36 -0
  7. data/lib/action_mcp/client/test_session_store.rb +84 -0
  8. data/lib/action_mcp/client/volatile_session_store.rb +38 -0
  9. data/lib/action_mcp/configuration.rb +16 -1
  10. data/lib/action_mcp/current.rb +19 -0
  11. data/lib/action_mcp/current_helpers.rb +19 -0
  12. data/lib/action_mcp/gateway.rb +85 -0
  13. data/lib/action_mcp/json_rpc_handler_base.rb +6 -1
  14. data/lib/action_mcp/jwt_decoder.rb +26 -0
  15. data/lib/action_mcp/prompt.rb +1 -0
  16. data/lib/action_mcp/resource_template.rb +1 -0
  17. data/lib/action_mcp/server/base_messaging.rb +14 -0
  18. data/lib/action_mcp/server/error_aware.rb +8 -1
  19. data/lib/action_mcp/server/handlers/tool_handler.rb +2 -1
  20. data/lib/action_mcp/server/json_rpc_handler.rb +12 -4
  21. data/lib/action_mcp/server/messaging.rb +12 -1
  22. data/lib/action_mcp/server/registry_management.rb +0 -1
  23. data/lib/action_mcp/server/response_collector.rb +40 -0
  24. data/lib/action_mcp/server/session_store.rb +762 -0
  25. data/lib/action_mcp/server/tools.rb +14 -3
  26. data/lib/action_mcp/server/transport_handler.rb +9 -5
  27. data/lib/action_mcp/server.rb +7 -0
  28. data/lib/action_mcp/tagged_stream_logging.rb +0 -4
  29. data/lib/action_mcp/test_helper/progress_notification_assertions.rb +105 -0
  30. data/lib/action_mcp/test_helper/session_store_assertions.rb +130 -0
  31. data/lib/action_mcp/test_helper.rb +4 -0
  32. data/lib/action_mcp/tool.rb +1 -0
  33. data/lib/action_mcp/version.rb +1 -1
  34. data/lib/action_mcp.rb +0 -1
  35. data/lib/generators/action_mcp/install/install_generator.rb +4 -0
  36. data/lib/generators/action_mcp/install/templates/application_gateway.rb +40 -0
  37. metadata +29 -1
@@ -39,14 +39,25 @@ module ActionMCP
39
39
  tool_class = session.registered_tools.find { |t| t.tool_name == tool_name }
40
40
 
41
41
  if tool_class
42
- # Create tool and set execution context
42
+ # Create tool and set execution context with request info
43
43
  tool = tool_class.new(arguments)
44
- tool.with_context({ session: session })
44
+ tool.with_context({
45
+ session: session,
46
+ request: {
47
+ params: {
48
+ name: tool_name,
49
+ arguments: arguments,
50
+ _meta: _meta
51
+ }
52
+ }
53
+ })
45
54
 
46
55
  result = tool.call
47
56
 
48
57
  if result.is_error
49
- send_jsonrpc_response(request_id, error: result)
58
+ # Convert ToolResponse error to proper JSON-RPC error format
59
+ error_hash = result.to_h
60
+ send_jsonrpc_response(request_id, error: error_hash)
50
61
  else
51
62
  send_jsonrpc_response(request_id, result: result)
52
63
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "response_collector"
4
+ require_relative "base_messaging"
5
+
3
6
  module ActionMCP
4
7
  module Server
5
8
  class TransportHandler
@@ -9,6 +12,7 @@ module ActionMCP
9
12
  delegate :read, :write, to: :session
10
13
  include Logging
11
14
 
15
+ include BaseMessaging # Provides basic write_message
12
16
  include Messaging
13
17
  include Capabilities
14
18
  include Tools
@@ -17,10 +21,14 @@ module ActionMCP
17
21
  include Notifications
18
22
  include Sampling
19
23
  include Roots
24
+ include ResponseCollector # Must be included last to override write_message
20
25
 
21
26
  # @param [ActionMCP::Session] session
22
- def initialize(session)
27
+ # @param messaging_mode [:write, :return] The mode for message handling
28
+ def initialize(session, messaging_mode: :write)
23
29
  @session = session
30
+ @messaging_mode = messaging_mode
31
+ initialize_response_collector if messaging_mode == :return
24
32
  end
25
33
 
26
34
  def send_pong(request_id)
@@ -29,10 +37,6 @@ module ActionMCP
29
37
 
30
38
  private
31
39
 
32
- def write_message(data)
33
- session.write(data)
34
- end
35
-
36
40
  def format_registry_items(registry)
37
41
  registry.map { |item| item.klass.to_h }
38
42
  end
@@ -28,6 +28,13 @@ module ActionMCP
28
28
  @server = nil
29
29
  end
30
30
 
31
+ # Access the session store
32
+ def session_store
33
+ @session_store ||= SessionStoreFactory.create(
34
+ ActionMCP.configuration.session_store_type
35
+ )
36
+ end
37
+
31
38
  # Available pubsub adapter types
32
39
  ADAPTERS = {
33
40
  "test" => "SimplePubSub",
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # frozen_string_literal: true
4
-
5
- # lib/action_mcp/tagged_io_logging.rb
6
-
7
3
  module ActionMCP
8
4
  module TaggedStreamLogging
9
5
  # ──────────── ANSI COLOURS ────────────
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module TestHelper
5
+ module ProgressNotificationAssertions
6
+ # Assert that at least one progress notification was sent with the given token
7
+ def assert_progress_notification_sent(token, message = nil)
8
+ notifications = get_progress_notifications(token)
9
+ assert notifications.any?,
10
+ message || "Expected at least one progress notification with token #{token}, but none were sent"
11
+ end
12
+
13
+ # Assert that no progress notifications were sent with the given token
14
+ def assert_no_progress_notification_sent(token, message = nil)
15
+ notifications = get_progress_notifications(token)
16
+ assert notifications.empty?,
17
+ message || "Expected no progress notifications with token #{token}, but #{notifications.size} were sent"
18
+ end
19
+
20
+ # Assert that progress values increase monotonically
21
+ def assert_progress_sequence_valid(token, message = nil)
22
+ notifications = get_progress_notifications(token)
23
+ progress_values = notifications.map { |n| n.params[:progress] }
24
+
25
+ assert progress_values.each_cons(2).all? { |a, b| b > a },
26
+ message || "Progress values must increase monotonically, but got: #{progress_values.inspect}"
27
+ end
28
+
29
+ # Assert specific fields in the latest progress notification
30
+ def assert_progress_notification_includes(token, expected, message = nil)
31
+ notifications = get_progress_notifications(token)
32
+ assert notifications.any?, "No progress notifications found for token #{token}"
33
+
34
+ notification = notifications.last
35
+ expected.each do |key, value|
36
+ actual = notification.params[key]
37
+ assert_equal value, actual,
38
+ message || "Expected notification to have #{key}: #{value.inspect}, but got: #{actual.inspect}"
39
+ end
40
+ end
41
+
42
+ # Assert the total count of progress notifications for a token
43
+ def assert_progress_notification_count(token, expected_count, message = nil)
44
+ notifications = get_progress_notifications(token)
45
+ actual_count = notifications.size
46
+
47
+ assert_equal expected_count, actual_count,
48
+ message || "Expected #{expected_count} progress notifications for token #{token}, but got #{actual_count}"
49
+ end
50
+
51
+ # Assert notification follows MCP spec structure
52
+ def assert_progress_notification_valid(notification, message = nil)
53
+ # Convert to hash if it's a notification object
54
+ data = notification.respond_to?(:to_h) ? notification.to_h : notification
55
+
56
+ # Verify JSON-RPC structure
57
+ assert_equal "2.0", data["jsonrpc"] || data[:jsonrpc],
58
+ message || "Notification must have jsonrpc: 2.0"
59
+ assert_equal "notifications/progress", data[:method],
60
+ message || "Notification method must be notifications/progress"
61
+ assert_nil data[:id],
62
+ message || "Notifications must not have an id field"
63
+
64
+ # Verify params
65
+ params = data[:params]
66
+ assert params, "Notification must have params"
67
+ assert params[:progressToken], "Notification must have progressToken"
68
+ assert params[:progress], "Notification must have progress value"
69
+
70
+ # Type checks
71
+ assert [ String, Integer ].include?(params[:progressToken].class),
72
+ "progressToken must be string or integer"
73
+ assert params[:progress].is_a?(Numeric),
74
+ "progress must be numeric"
75
+
76
+ # Optional field type checks if present
77
+ if params.key?(:total)
78
+ assert params[:total].is_a?(Numeric),
79
+ "total must be numeric when present"
80
+ end
81
+
82
+ if params.key?(:message)
83
+ assert params[:message].is_a?(String),
84
+ "message must be string when present"
85
+ end
86
+ end
87
+
88
+ # Get the current session store (with helpful error if not using test store)
89
+ def progress_session_store
90
+ store = ActionMCP::Server.session_store
91
+ unless store.respond_to?(:notifications_for_token)
92
+ raise "Session store #{store.class} does not support notification tracking. " \
93
+ "Use TestSessionStore for progress notification tests."
94
+ end
95
+ store
96
+ end
97
+
98
+ private
99
+
100
+ def get_progress_notifications(token)
101
+ progress_session_store.notifications_for_token(token)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module TestHelper
5
+ module SessionStoreAssertions
6
+ # Server session store assertions
7
+ def assert_session_created(session_id, message = nil)
8
+ assert server_session_store.session_created?(session_id),
9
+ message || "Expected session #{session_id} to have been created"
10
+ end
11
+
12
+ def assert_session_not_created(session_id, message = nil)
13
+ assert_not server_session_store.session_created?(session_id),
14
+ message || "Expected session #{session_id} not to have been created"
15
+ end
16
+
17
+ def assert_session_loaded(session_id, message = nil)
18
+ assert server_session_store.session_loaded?(session_id),
19
+ message || "Expected session #{session_id} to have been loaded"
20
+ end
21
+
22
+ def assert_session_not_loaded(session_id, message = nil)
23
+ assert_not server_session_store.session_loaded?(session_id),
24
+ message || "Expected session #{session_id} not to have been loaded"
25
+ end
26
+
27
+ def assert_session_saved(session_id, message = nil)
28
+ assert server_session_store.session_saved?(session_id),
29
+ message || "Expected session #{session_id} to have been saved"
30
+ end
31
+
32
+ def assert_session_not_saved(session_id, message = nil)
33
+ assert_not server_session_store.session_saved?(session_id),
34
+ message || "Expected session #{session_id} not to have been saved"
35
+ end
36
+
37
+ def assert_session_deleted(session_id, message = nil)
38
+ assert server_session_store.session_deleted?(session_id),
39
+ message || "Expected session #{session_id} to have been deleted"
40
+ end
41
+
42
+ def assert_session_not_deleted(session_id, message = nil)
43
+ assert_not server_session_store.session_deleted?(session_id),
44
+ message || "Expected session #{session_id} not to have been deleted"
45
+ end
46
+
47
+ def assert_session_operation_count(expected, type = nil, message = nil)
48
+ actual = server_session_store.operation_count(type)
49
+ type_desc = type ? " of type #{type}" : ""
50
+ assert_equal expected, actual,
51
+ message || "Expected #{expected} session operations#{type_desc}, got #{actual}"
52
+ end
53
+
54
+ # Client session store assertions
55
+ def assert_client_session_saved(session_id, message = nil)
56
+ assert client_session_store.session_saved?(session_id),
57
+ message || "Expected client session #{session_id} to have been saved"
58
+ end
59
+
60
+ def assert_client_session_not_saved(session_id, message = nil)
61
+ assert_not client_session_store.session_saved?(session_id),
62
+ message || "Expected client session #{session_id} not to have been saved"
63
+ end
64
+
65
+ def assert_client_session_loaded(session_id, message = nil)
66
+ assert client_session_store.session_loaded?(session_id),
67
+ message || "Expected client session #{session_id} to have been loaded"
68
+ end
69
+
70
+ def assert_client_session_not_loaded(session_id, message = nil)
71
+ assert_not client_session_store.session_loaded?(session_id),
72
+ message || "Expected client session #{session_id} not to have been loaded"
73
+ end
74
+
75
+ def assert_client_session_updated(session_id, message = nil)
76
+ assert client_session_store.session_updated?(session_id),
77
+ message || "Expected client session #{session_id} to have been updated"
78
+ end
79
+
80
+ def assert_client_session_not_updated(session_id, message = nil)
81
+ assert_not client_session_store.session_updated?(session_id),
82
+ message || "Expected client session #{session_id} not to have been updated"
83
+ end
84
+
85
+ def assert_client_session_deleted(session_id, message = nil)
86
+ assert client_session_store.session_deleted?(session_id),
87
+ message || "Expected client session #{session_id} to have been deleted"
88
+ end
89
+
90
+ def assert_client_session_not_deleted(session_id, message = nil)
91
+ assert_not client_session_store.session_deleted?(session_id),
92
+ message || "Expected client session #{session_id} not to have been deleted"
93
+ end
94
+
95
+ def assert_client_session_operation_count(expected, type = nil, message = nil)
96
+ actual = client_session_store.operation_count(type)
97
+ type_desc = type ? " of type #{type}" : ""
98
+ assert_equal expected, actual,
99
+ message || "Expected #{expected} client session operations#{type_desc}, got #{actual}"
100
+ end
101
+
102
+ def assert_client_session_data_includes(session_id, expected_data, message = nil)
103
+ saved_data = client_session_store.last_saved_data(session_id)
104
+ assert saved_data, "No saved data found for session #{session_id}"
105
+
106
+ expected_data.each do |key, value|
107
+ assert_equal value, saved_data[key],
108
+ message || "Expected session #{session_id} data to include #{key}: #{value}"
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def server_session_store
115
+ store = ActionMCP::Server.session_store
116
+ raise "Server session store is not a TestSessionStore" unless store.is_a?(ActionMCP::Server::TestSessionStore)
117
+ store
118
+ end
119
+
120
+ def client_session_store
121
+ # This would need to be set by the test or could use a thread-local variable
122
+ # For now, we'll assume it's available as an instance variable
123
+ store = @client_session_store || Thread.current[:test_client_session_store]
124
+ raise "Client session store not set. Set @client_session_store or Thread.current[:test_client_session_store]" unless store
125
+ raise "Client session store is not a TestSessionStore" unless store.is_a?(ActionMCP::Client::TestSessionStore)
126
+ store
127
+ end
128
+ end
129
+ end
130
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/testing/assertions"
4
+ require_relative "test_helper/session_store_assertions"
5
+ require_relative "test_helper/progress_notification_assertions"
4
6
 
5
7
  module ActionMCP
6
8
  #---------------------------------------------------------------------------
@@ -23,6 +25,8 @@ module ActionMCP
23
25
  #---------------------------------------------------------------------------
24
26
  module TestHelper
25
27
  include ActiveSupport::Testing::Assertions
28
+ include SessionStoreAssertions
29
+ include ProgressNotificationAssertions
26
30
 
27
31
  # ──── Registry assertions ────────────────────────────────────────────────
28
32
  def assert_mcp_tool_findable(name, msg = nil)
@@ -7,6 +7,7 @@ module ActionMCP
7
7
  # Tools are registered automatically in the ToolsRegistry unless marked as abstract.
8
8
  class Tool < Capability
9
9
  include ActionMCP::Callbacks
10
+ include ActionMCP::CurrentHelpers
10
11
  # --------------------------------------------------------------------------
11
12
  # Class Attributes for Tool Metadata and Schema
12
13
  # --------------------------------------------------------------------------
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "gem_version"
4
4
  module ActionMCP
5
- VERSION = "0.51.0"
5
+ VERSION = "0.52.1"
6
6
 
7
7
  class << self
8
8
  alias version gem_version
data/lib/action_mcp.rb CHANGED
@@ -8,7 +8,6 @@ require "concurrent"
8
8
  require "active_record/railtie"
9
9
  require "jsonrpc-rails"
10
10
  require "action_controller/railtie"
11
- require "action_cable/engine"
12
11
  require "action_mcp/configuration"
13
12
  require "action_mcp/log_subscriber"
14
13
  require "action_mcp/engine"
@@ -23,6 +23,10 @@ module ActionMcp
23
23
  def create_mcp_profile_file
24
24
  template "mcp.yml", File.join("config", "mcp.yml")
25
25
  end
26
+
27
+ def create_application_gateway_file
28
+ template "application_gateway.rb", File.join("app/mcp", "application_gateway.rb")
29
+ end
26
30
  end
27
31
  end
28
32
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationGateway < ActionMCP::Gateway
4
+ # Specify what attributes identify a connection
5
+ # Multiple identifiers can be used (e.g., user, account, organization)
6
+ identified_by :user
7
+
8
+ protected
9
+
10
+ # Override this method to implement your authentication logic
11
+ # Must return a hash with keys matching the identified_by attributes
12
+ # or raise ActionMCP::UnauthorizedError
13
+ def authenticate!
14
+ # Example using JWT:
15
+ token = extract_bearer_token
16
+ raise ActionMCP::UnauthorizedError, "Missing token" unless token
17
+
18
+ payload = ActionMCP::JwtDecoder.decode(token)
19
+ user = resolve_user(payload)
20
+
21
+ raise ActionMCP::UnauthorizedError, "Unauthorized" unless user
22
+
23
+ # Return a hash with all identified_by attributes
24
+ { user: user }
25
+ rescue ActionMCP::JwtDecoder::DecodeError => e
26
+ raise ActionMCP::UnauthorizedError, e.message
27
+ end
28
+
29
+ private
30
+
31
+ # Example method to resolve user from JWT payload
32
+ def resolve_user(payload)
33
+ return nil unless payload.is_a?(Hash)
34
+ user_id = payload["user_id"] || payload["sub"]
35
+ return nil unless user_id
36
+
37
+ # Replace with your User model lookup
38
+ User.find_by(id: user_id)
39
+ end
40
+ end
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.51.0
4
+ version: 0.52.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -93,6 +93,20 @@ dependencies:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  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'
96
110
  description: It offers base classes and helpers for creating MCP applications, making
97
111
  it easier to integrate your Ruby/Rails application with the MCP standard
98
112
  email:
@@ -123,6 +137,7 @@ files:
123
137
  - lib/action_mcp/callbacks.rb
124
138
  - lib/action_mcp/capability.rb
125
139
  - lib/action_mcp/client.rb
140
+ - lib/action_mcp/client/active_record_session_store.rb
126
141
  - lib/action_mcp/client/base.rb
127
142
  - lib/action_mcp/client/blueprint.rb
128
143
  - lib/action_mcp/client/catalog.rb
@@ -137,11 +152,14 @@ files:
137
152
  - lib/action_mcp/client/roots.rb
138
153
  - lib/action_mcp/client/server.rb
139
154
  - lib/action_mcp/client/session_store.rb
155
+ - lib/action_mcp/client/session_store_factory.rb
140
156
  - lib/action_mcp/client/sse_client.rb
141
157
  - lib/action_mcp/client/streamable_http_transport.rb
158
+ - lib/action_mcp/client/test_session_store.rb
142
159
  - lib/action_mcp/client/toolbox.rb
143
160
  - lib/action_mcp/client/tools.rb
144
161
  - lib/action_mcp/client/transport.rb
162
+ - lib/action_mcp/client/volatile_session_store.rb
145
163
  - lib/action_mcp/configuration.rb
146
164
  - lib/action_mcp/console_detector.rb
147
165
  - lib/action_mcp/content.rb
@@ -150,13 +168,17 @@ files:
150
168
  - lib/action_mcp/content/image.rb
151
169
  - lib/action_mcp/content/resource.rb
152
170
  - lib/action_mcp/content/text.rb
171
+ - lib/action_mcp/current.rb
172
+ - lib/action_mcp/current_helpers.rb
153
173
  - lib/action_mcp/engine.rb
174
+ - lib/action_mcp/gateway.rb
154
175
  - lib/action_mcp/gem_version.rb
155
176
  - lib/action_mcp/instrumentation/controller_runtime.rb
156
177
  - lib/action_mcp/instrumentation/instrumentation.rb
157
178
  - lib/action_mcp/instrumentation/resource_instrumentation.rb
158
179
  - lib/action_mcp/integer_array.rb
159
180
  - lib/action_mcp/json_rpc_handler_base.rb
181
+ - lib/action_mcp/jwt_decoder.rb
160
182
  - lib/action_mcp/log_subscriber.rb
161
183
  - lib/action_mcp/logging.rb
162
184
  - lib/action_mcp/prompt.rb
@@ -169,6 +191,7 @@ files:
169
191
  - lib/action_mcp/resource_template.rb
170
192
  - lib/action_mcp/resource_templates_registry.rb
171
193
  - lib/action_mcp/server.rb
194
+ - lib/action_mcp/server/base_messaging.rb
172
195
  - lib/action_mcp/server/capabilities.rb
173
196
  - lib/action_mcp/server/configuration.rb
174
197
  - lib/action_mcp/server/error_aware.rb
@@ -182,9 +205,11 @@ files:
182
205
  - lib/action_mcp/server/prompts.rb
183
206
  - lib/action_mcp/server/registry_management.rb
184
207
  - lib/action_mcp/server/resources.rb
208
+ - lib/action_mcp/server/response_collector.rb
185
209
  - lib/action_mcp/server/roots.rb
186
210
  - lib/action_mcp/server/sampling.rb
187
211
  - lib/action_mcp/server/sampling_request.rb
212
+ - lib/action_mcp/server/session_store.rb
188
213
  - lib/action_mcp/server/simple_pub_sub.rb
189
214
  - lib/action_mcp/server/solid_cable_adapter.rb
190
215
  - lib/action_mcp/server/tools.rb
@@ -193,6 +218,8 @@ files:
193
218
  - lib/action_mcp/string_array.rb
194
219
  - lib/action_mcp/tagged_stream_logging.rb
195
220
  - lib/action_mcp/test_helper.rb
221
+ - lib/action_mcp/test_helper/progress_notification_assertions.rb
222
+ - lib/action_mcp/test_helper/session_store_assertions.rb
196
223
  - lib/action_mcp/tool.rb
197
224
  - lib/action_mcp/tool_response.rb
198
225
  - lib/action_mcp/tools_registry.rb
@@ -203,6 +230,7 @@ files:
203
230
  - lib/generators/action_mcp/config/config_generator.rb
204
231
  - lib/generators/action_mcp/config/templates/mcp.yml
205
232
  - lib/generators/action_mcp/install/install_generator.rb
233
+ - lib/generators/action_mcp/install/templates/application_gateway.rb
206
234
  - lib/generators/action_mcp/install/templates/application_mcp_prompt.rb
207
235
  - lib/generators/action_mcp/install/templates/application_mcp_res_template.rb
208
236
  - lib/generators/action_mcp/install/templates/application_mcp_tool.rb