actionmcp 0.2.6 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15695f4c672c4af6d7b6114a92d395af80f012da1221815237d12dd21d1dee31
4
- data.tar.gz: 5c446ff697530b0600e0a176360de1653536e86eadff979a35149bf62821c4d1
3
+ metadata.gz: 529a0b8315fdd1d869ab3310d18c9a0ecb316eb3b1b5ab54f13ad93588e55152
4
+ data.tar.gz: 1dc90c03f28134264025649f4fa7b4a3ed97d98c7375c437ddcb0548461aa2cd
5
5
  SHA512:
6
- metadata.gz: 936f8873dcb2fb25fd57880107d768672655a1c75df41fb26f7a7d9be34f3bff289bb671afff31a5dcef4a8aca3398413622791b2c6c1ad1cc2429209fc5ac8f
7
- data.tar.gz: 81e347ed22f4f2d2fa6bb0d6bbb91e7c1b3a89b2295b5635872e4f90107183b5dab658cb20973e41c080977e361a90a16b1cf7ca582947600f70050abf060901
6
+ metadata.gz: 2e7abf47b0ed09eff25c2d2097f4654ae8d4766810f458cad51ab217698a206cbb7375ecaf4b3bd23db70fa6fd31d4c34ac4db6f2ec723187f68deb8bca94e91
7
+ data.tar.gz: da41afe6699282401e85b9981c29f0c04906dce1b5574096c456d7508f9866ef6cb396c642c32b3f78e96f68d50c95e7d0077dccc6bd5e1dacf5816d8a1978c8
data/README.md CHANGED
@@ -91,8 +91,8 @@ bin/rails generate action_mcp:install
91
91
  ```
92
92
 
93
93
  This command will create:
94
- - `app/prompts/application_prompt.rb`
95
- - `app/tools/application_tool.rb`
94
+ - `app/mcp/prompts/application_prompt.rb`
95
+ - `app/mcp/tools/application_tool.rb`
96
96
 
97
97
  #### Generate a New Prompt
98
98
 
@@ -102,7 +102,7 @@ Run the following command to generate a new prompt class:
102
102
  bin/rails generate action_mcp:prompt AnalyzeCode
103
103
  ```
104
104
 
105
- This command will create a file at `app/prompts/analyze_code_prompt.rb` with content similar to:
105
+ This command will create a file at `app/mcp/prompts/analyze_code_prompt.rb` with content similar to:
106
106
 
107
107
  ```ruby
108
108
  class AnalyzeCodePrompt < ApplicationPrompt
@@ -121,7 +121,7 @@ class AnalyzeCodePrompt < ApplicationPrompt
121
121
 
122
122
  def call
123
123
  # Implement your prompt logic here
124
- render_text("Analyzing #{language} code: #{code}")
124
+ render(text: "Analyzing #{language} code: #{code}")
125
125
  end
126
126
  end
127
127
  ```
@@ -134,7 +134,7 @@ Similarly, run the following command to generate a new tool class:
134
134
  bin/rails generate action_mcp:tool CalculateSum
135
135
  ```
136
136
 
137
- This command will create a file at `app/tools/calculate_sum_tool.rb` with content similar to:
137
+ This command will create a file at `app/mcp/tools/calculate_sum_tool.rb` with content similar to:
138
138
 
139
139
  ```ruby
140
140
  class CalculateSumTool < ApplicationTool
@@ -145,7 +145,7 @@ class CalculateSumTool < ApplicationTool
145
145
  property :b, type: "number", description: "Second number", required: true
146
146
 
147
147
  def call
148
- render_text(a + b)
148
+ render(text: a + b)
149
149
  end
150
150
  end
151
151
  ```
@@ -189,7 +189,6 @@ end
189
189
  ```ruby
190
190
  # Instantiate the tool with initial values
191
191
  sum_tool = CalculateSumTool.new(a: 5, b: 10)
192
-
193
192
  # Optionally update attributes later:
194
193
  sum_tool.a = 15
195
194
  sum_tool.b = 20
@@ -232,6 +231,46 @@ These examples show that both prompts and tools follow a consistent pattern for
232
231
  - **API Stability:**
233
232
  The ActionMCP API is stable, though it is acceptable for improvements and changes to be introduced as we move forward. This approach ensures the gem stays modern and adaptable to evolving requirements.
234
233
 
234
+ ## Testing with the TestHelper
235
+
236
+ ActionMCP provides a `TestHelper` module to simplify testing of tools and prompts. To use the `TestHelper`, include it in your test class:
237
+
238
+ ```ruby
239
+ require "test_helper"
240
+ require "action_mcp/test_helper"
241
+
242
+ class ToolTest < ActiveSupport::TestCase
243
+ include ActionMCP::TestHelper
244
+
245
+ test "CalculateSumTool returns the correct sum" do
246
+ assert_tool_findable("calculate_sum")
247
+ result = execute_tool("calculate_sum", a: 5, b: 10)
248
+ assert_tool_output(result, "15.0")
249
+ end
250
+
251
+ test "AnalyzeCodePrompt returns the correct analysis" do
252
+ assert_prompt_findable("analyze_code")
253
+ result = execute_tool("analyze_code", language: "Ruby", code: "def hello; puts 'Hello, world!'; end")
254
+ assert_equal "Analyzing Ruby code: def hello; puts 'Hello, world!'; end", assert_prompt_output(result)
255
+ end
256
+ end
257
+ ```
258
+
259
+ The `TestHelper` module provides the following methods:
260
+
261
+ * `assert_tool_findable(tool_name)`: Asserts that a tool is findable in the `ToolsRegistry`.
262
+ * `assert_prompt_findable(prompt_name)`: Asserts that a prompt is findable in the `PromptsRegistry`.
263
+ * `execute_tool(tool_name, args = {})`: Executes a tool with the given name and arguments.
264
+ * `execute_prompt(prompt_name, args = {})`: Executes a prompt with the given name and arguments.
265
+ * `assert_tool_output(result, expected_output)`: Asserts that the output of a tool is equal to the expected output.
266
+ * `assert_prompt_output(result)`: Asserts that the output of a prompt is equal to the expected output.
267
+
268
+ To use the `TestHelper`, you need to require it in your `test_helper.rb` file:
269
+
270
+ ```ruby
271
+ require "action_mcp/test_helper"
272
+ ```
273
+
235
274
  ## Conclusion
236
275
 
237
- ActionMCP empowers developers to build MCP-compliant servers efficiently by handling the standardization and boilerplate associated with integrating with LLMs. With built-in generators, clear configuration options, robust usage examples, and important deployment considerations, it is designed to accelerate development and integration work while remaining flexible for future enhancements.
276
+ ActionMCP empowers developers to build MCP-compliant servers efficiently by handling the standardization and boilerplate associated with integrating with LLMs. With built-in generators, clear configuration options, robust usage examples, important deployment considerations, and a helpful testing module, it is designed to accelerate development and integration work while remaining flexible for future enhancements.
@@ -17,7 +17,8 @@ module ActionMCP
17
17
  validates :protocol_version, inclusion: { in: [ PROTOCOL_VERSION ] }, allow_nil: true
18
18
 
19
19
  def close!
20
- adapter.unsubscribe(session_key, _)
20
+ dummy_callback = ->(*) { } # this callback seem broken
21
+ adapter.unsubscribe(session_key, dummy_callback)
21
22
  update!(status: "closed", ended_at: Time.zone.now)
22
23
  end
23
24
 
@@ -33,7 +34,6 @@ module ActionMCP
33
34
  end
34
35
 
35
36
  def read(data)
36
- puts "\e[33m[#{role}] #{data}\e[0m"
37
37
  messages.create!(data: data, direction: role)
38
38
  end
39
39
 
@@ -40,11 +40,12 @@ module ActionMCP
40
40
  def capabilities
41
41
  capabilities = {}
42
42
  # Only include each capability if the corresponding registry is non-empty.
43
- capabilities[:tools] = { listChanged: @list_changed } if ToolsRegistry.non_abstract.any?
44
- capabilities[:prompts] = { listChanged: @list_changed } if PromptsRegistry.non_abstract.any?
43
+ capabilities[:tools] = { listChanged: false } if ToolsRegistry.non_abstract.any?
44
+ capabilities[:prompts] = { listChanged: false } if PromptsRegistry.non_abstract.any?
45
45
  capabilities[:logging] = {} if @logging_enabled
46
- # capabilities[:resources] = { subscribe: @resources_subscribe,
47
- # listChanged: @list_changed }.compact
46
+ # For now, we only have one type of resource, ResourceTemplate
47
+ # For Resources, we need to think about how to pass the list to the session.
48
+ capabilities[:resources] = {} if ResourceTemplatesRegistry.non_abstract.any?
48
49
  capabilities
49
50
  end
50
51
  end
@@ -30,5 +30,16 @@ module ActionMCP
30
30
  self.logger = ::Rails.logger
31
31
  end
32
32
  end
33
+
34
+ # Configure autoloading for the mcp/tools directory
35
+ initializer "action_mcp.autoloading" do |app|
36
+ mcp_path = Rails.root.join("app/mcp")
37
+
38
+ if Dir.exist?(mcp_path)
39
+ Dir.glob(mcp_path.join("*")).select { |f| File.directory?(f) }.each do |dir|
40
+ Rails.autoloaders.main.push_dir(dir, namespace: Object)
41
+ end
42
+ end
43
+ end
33
44
  end
34
45
  end
@@ -42,38 +42,40 @@ module ActionMCP
42
42
  return if request["error"]
43
43
  return if request["result"] == {} # Probably a pong
44
44
 
45
- method = request["method"]
45
+ rpc_method = request["method"]
46
46
  id = request["id"]
47
47
  params = request["params"]
48
48
 
49
- case method
49
+ case rpc_method
50
50
  when "initialize"
51
- puts "\e[31mSending capabilities\e[0m"
52
51
  transport.send_capabilities(id, params)
53
52
  when "ping"
54
53
  transport.send_pong(id)
55
54
  when /^notifications\//
56
55
  puts "\e[31mProcessing notifications\e[0m"
57
- process_notifications(method)
56
+ process_notifications(rpc_method, params)
58
57
  when /^prompts\//
59
- process_prompts(method, id, params)
58
+ process_prompts(rpc_method, id, params)
60
59
  when /^resources\//
61
- process_resources(method, id, params)
60
+ process_resources(rpc_method, id, params)
62
61
  when /^tools\//
63
- process_tools(method, id, params)
62
+ process_tools(rpc_method, id, params)
64
63
  when "completion/complete"
65
64
  process_completion_complete(id, params)
66
65
  else
67
- puts "\e[31mUnknown method: #{method} #{request}\e[0m"
66
+ puts "\e[31mUnknown method: #{rpc_method} #{request}\e[0m"
68
67
  end
69
68
  end
70
69
 
71
70
  # @param rpc_method [String]
72
- def process_notifications(rpc_method)
71
+ def process_notifications(rpc_method, params)
73
72
  case rpc_method
74
73
  when "notifications/initialized"
75
74
  puts "\e[31mInitialized\e[0m"
76
75
  transport.initialize!
76
+ when "notifications/cancelled"
77
+ puts "\e[31m Request #{params["requestId"]} cancelled: #{params["reason"]}\e[0m"
78
+ # we don't need to do anything here
77
79
  else
78
80
  Rails.logger.warn("Unknown notifications method: #{rpc_method}")
79
81
  end
@@ -3,52 +3,23 @@
3
3
  module ActionMCP
4
4
  # Module for rendering content.
5
5
  module Renderable
6
- # Renders text content.
7
- #
8
- # @param text [String] The text to render.
9
- # @return [Content::Text] The rendered text content.
10
- def render_text(text)
11
- Content::Text.new(text)
12
- end
13
-
14
- # Renders audio content.
15
- #
16
- # @param data [String] The audio data.
17
- # @param mime_type [String] The MIME type of the audio data.
18
- # @return [Content::Audio] The rendered audio content.
19
- def render_audio(data, mime_type)
20
- Content::Audio.new(data, mime_type)
21
- end
22
-
23
- # Renders image content.
24
- #
25
- # @param data [String] The image data.
26
- # @param mime_type [String] The MIME type of the image data.
27
- # @return [Content::Image] The rendered image content.
28
- def render_image(data, mime_type)
29
- Content::Image.new(data, mime_type)
30
- end
31
-
32
- # Renders a resource.
33
- #
34
- # @param uri [String] The URI of the resource.
35
- # @param mime_type [String] The MIME type of the resource.
36
- # @param text [String, nil] The text associated with the resource.
37
- # @param blob [String, nil] The blob associated with the resource.
38
- # @return [Content::Resource] The rendered resource content.
39
- def render_resource(uri, mime_type, text: nil, blob: nil)
40
- Content::Resource.new(uri, mime_type, text: text, blob: blob)
41
- end
42
-
43
- # Renders an error.
44
- #
45
- # @param errors [Array<String>] The errors to render.
46
- # @return [Hash] A hash containing the error information.
47
- def render_error(errors)
48
- {
49
- isError: true,
50
- content: errors.map { |error| render_text(error) }
51
- }
6
+ def render(text: nil, audio: nil, image: nil, resource: nil, error: nil, mime_type: nil, uri: nil, blob: nil)
7
+ if text
8
+ Content::Text.new(text)
9
+ elsif audio && mime_type
10
+ Content::Audio.new(audio, mime_type)
11
+ elsif image && mime_type
12
+ Content::Image.new(image, mime_type)
13
+ elsif resource && uri && mime_type
14
+ Content::Resource.new(uri, mime_type, text: text, blob: blob)
15
+ elsif error
16
+ {
17
+ isError: true,
18
+ content: error.map { |e| render(text: e) }
19
+ }
20
+ else
21
+ raise ArgumentError, "No content to render"
22
+ end
52
23
  end
53
24
  end
54
25
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ class ResourceTemplate
5
+ class_attribute :abstract, instance_accessor: false, default: false
6
+
7
+ attr_reader :description, :uri_template, :mime_type
8
+
9
+ def self.parameter(name, description:, required: false)
10
+ @parameters ||= {}
11
+ @parameters[name] = { description: description, required: required }
12
+ end
13
+
14
+ def self.parameters
15
+ @parameters || {}
16
+ end
17
+
18
+ def self.description(description = nil)
19
+ return @description unless description
20
+ @description = description
21
+ end
22
+
23
+ def self.to_h
24
+ name_value = defined?(@template_name) ? @template_name : name.demodulize.underscore.gsub(/_template$/, '')
25
+
26
+ {
27
+ uriTemplate: @uri_template,
28
+ name: name_value,
29
+ description: @description,
30
+ mimeType: @mime_type
31
+ }.compact
32
+ end
33
+
34
+ def self.uri_template(uri_template = nil)
35
+ return @uri_template unless uri_template
36
+ @uri_template = uri_template
37
+ end
38
+
39
+ def self.template_name(name = nil)
40
+ @template_name = name if name
41
+ @template_name
42
+ end
43
+
44
+ def self.mime_type(mime_type = nil)
45
+ return @mime_type unless mime_type
46
+ @mime_type = mime_type
47
+ end
48
+
49
+ def self.retrieve(_params)
50
+ raise NotImplementedError, "Subclasses must implement the retrieve method"
51
+ end
52
+
53
+ def self.abstract
54
+ @abstract_tool ||= false # Default to false, unique to each class
55
+ end
56
+
57
+ def self.abstract=(value)
58
+ @abstract_tool = value
59
+ end
60
+
61
+ def self.abstract!
62
+ self.abstract = true
63
+ end
64
+
65
+ def self.abstract?
66
+ abstract
67
+ end
68
+
69
+ def self.capability_name
70
+ name.demodulize.underscore.sub(/_template$/, "")
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ # Registry for managing resource templates.
5
+ class ResourceTemplatesRegistry < RegistryBase
6
+ class << self
7
+ # @!method resource_templates
8
+ # Returns all registered resource templates.
9
+ # @return [Hash] A hash of registered resource templates.
10
+ alias resource_templates items
11
+
12
+ # Retrieves a resource template by name.
13
+ #
14
+ # @param template_name [String] The name of the resource template to retrieve.
15
+ # @return [ActionMCP::ResourceTemplate] The resource template.
16
+ # @raise [RegistryBase::NotFound] if the resource template is not found.
17
+ def get_resource_template(template_name)
18
+ find(template_name)
19
+ end
20
+
21
+ def item_klass
22
+ ResourceTemplate
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ require "active_support/testing/assertions"
2
+
3
+ module ActionMCP
4
+ module TestHelper
5
+ include ActiveSupport::Testing::Assertions
6
+
7
+ def assert_tool_findable(tool_name)
8
+ assert ActionMCP::ToolsRegistry.tools.key?(tool_name), "Tool #{tool_name} not found in registry"
9
+ end
10
+
11
+ def assert_prompt_findable(prompt_name)
12
+ assert ActionMCP::PromptsRegistry.prompts.key?(prompt_name), "Prompt #{prompt_name} not found in registry"
13
+ end
14
+
15
+ def execute_tool(tool_name, args = {})
16
+ result = ActionMCP::ToolsRegistry.tool_call(tool_name, args)
17
+ assert_equal false, result[:isError], "Tool #{tool_name} returned an error: #{result[:content].map(&:text).join(', ')}" if result[:isError]
18
+ result
19
+ end
20
+
21
+ def execute_prompt(prompt_name, args = {})
22
+ result = ActionMCP::PromptsRegistry.prompt_call(prompt_name, args)
23
+ assert_equal false, result[:isError], "Prompt #{prompt_name} returned an error: #{result[:content].map(&:text).join(', ')}" if result[:isError]
24
+ result
25
+ end
26
+
27
+ def assert_tool_output(result, expected_output)
28
+ assert_equal expected_output, result[:content][0].text
29
+ end
30
+
31
+ def assert_prompt_output(result)
32
+ assert_equal "user", result[:messages][0][:role]
33
+ result[:messages][0][:content]
34
+ end
35
+
36
+ # Add more assertion methods as needed
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMCP
4
+ module Transport
5
+ module Resources
6
+ def send_resources_list(request_id)
7
+ send_jsonrpc_response(request_id, result: { resources: [] })
8
+ end
9
+
10
+ def send_resource_templates_list(request_id)
11
+ templates = ActionMCP::ResourceTemplatesRegistry.resource_templates.values.map do |template|
12
+ template.to_h
13
+ end
14
+ # TODO add pagination support
15
+ # TODO add autocomplete
16
+ log_resource_templates
17
+ send_jsonrpc_response(request_id, result: { resourceTemplates: templates })
18
+ end
19
+
20
+ def send_resource_read(id, params)
21
+ send_jsonrpc_response(id, result: {})
22
+ end
23
+
24
+ def log_resource_templates
25
+ Rails.logger.info("Registered Resource Templates: #{ActionMCP::ResourceTemplatesRegistry.resource_templates.keys}")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -10,6 +10,7 @@ module ActionMCP
10
10
  include Transport::Capabilities
11
11
  include Transport::Tools
12
12
  include Transport::Prompts
13
+ include Transport::Resources
13
14
  include Transport::Messaging
14
15
 
15
16
  HEARTBEAT_INTERVAL = 15 # seconds
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "gem_version"
4
4
  module ActionMCP
5
- VERSION = "0.2.6"
5
+ VERSION = "0.4.0"
6
6
 
7
7
  class << self
8
8
  alias version gem_version
@@ -1,17 +1,20 @@
1
- # frozen_string_literal: true
1
+ require "rails/generators"
2
2
 
3
- module ActionMCP
3
+ module ActionMcp
4
4
  module Generators
5
5
  class InstallGenerator < Rails::Generators::Base
6
- namespace "action_mcp:install"
7
6
  source_root File.expand_path("templates", __dir__)
8
- desc "Installs both ApplicationPrompt and ApplicationTool"
9
- def create_application_prompt
10
- template "application_prompt.rb", "app/prompts/application_prompt.rb"
7
+
8
+ def create_application_prompt_file
9
+ template "application_prompt.rb", File.join("app/mcp/prompts", "application_prompt.rb")
10
+ end
11
+
12
+ def create_application_tool_file
13
+ template "application_tool.rb", File.join("app/mcp/tools", "application_tool.rb")
11
14
  end
12
15
 
13
- def create_application_tool
14
- template "application_tool.rb", "app/tools/application_tool.rb"
16
+ def create_mcp_resource_template_file
17
+ template "mcp_resource_template.rb", File.join("app/mcp/resource_templates", "mcp_resource_template.rb")
15
18
  end
16
19
  end
17
20
  end
@@ -0,0 +1,3 @@
1
+ class MCPResourceTemplate < ActionMcp::ResourceTemplate
2
+ abstract!
3
+ end
@@ -5,13 +5,13 @@ module ActionMCP
5
5
  class PromptGenerator < Rails::Generators::Base
6
6
  namespace "action_mcp:prompt"
7
7
  source_root File.expand_path("templates", __dir__)
8
- desc "Creates a Prompt (in app/prompts) that inherits from ApplicationPrompt"
8
+ desc "Creates a Prompt (in app/mcp/prompts) that inherits from ApplicationPrompt"
9
9
 
10
10
  # The generator takes one argument, e.g. "AnalyzeCode"
11
11
  argument :name, type: :string, required: true, banner: "PromptName"
12
12
 
13
13
  def create_prompt_file
14
- template "prompt.rb.erb", "app/prompts/#{file_name}.rb"
14
+ template "prompt.rb.erb", "app/mcp/prompts/#{file_name}.rb"
15
15
  end
16
16
 
17
17
  private
@@ -0,0 +1,28 @@
1
+ require "rails/generators"
2
+
3
+ module ActionMcp
4
+ module Generators
5
+ class ResourceTemplateGenerator < Rails::Generators::NamedBase
6
+ namespace "action_mcp:resource_template"
7
+ source_root File.expand_path("templates", __dir__)
8
+ desc "Creates a ResourceTemplate (in app/mcp/resource_templates) that inherits from MCPResourceTemplate"
9
+
10
+ argument :name, type: :string, required: true, banner: "ResourceTemplateName"
11
+
12
+ def create_resource_template_file
13
+ template "resource_template.rb.erb", "app/mcp/resource_templates/#{file_name}.rb"
14
+ end
15
+
16
+ private
17
+
18
+ def class_name
19
+ "#{name.camelize}#{name.camelize.end_with?('Template') ? '' : 'Template'}"
20
+ end
21
+
22
+ def file_name
23
+ base = name.underscore
24
+ base.end_with?("_template") ? base : "#{base}_template"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ class <%= class_name %> < MCPResourceTemplate
2
+ template_name "product"
3
+ description "Access product information"
4
+ uri_template "app://products/{product_id}"
5
+ mime_type "application/json"
6
+
7
+ parameter :product_id,
8
+ description: "Product identifier",
9
+ required: true
10
+
11
+ def self.retrieve(params)
12
+ raise NotImplementedError, "resolve must be implemented in the subclass"
13
+ end
14
+ end
@@ -12,6 +12,6 @@ class <%= class_name %> < ApplicationTool
12
12
 
13
13
  # Implement the tool's logic here.
14
14
  def call
15
- render_text(a + b)
15
+ render(text: a + b)
16
16
  end
17
17
  end
@@ -5,13 +5,13 @@ module ActionMCP
5
5
  class ToolGenerator < Rails::Generators::Base
6
6
  namespace "action_mcp:tool"
7
7
  source_root File.expand_path("templates", __dir__)
8
- desc "Creates a Tool (in app/tools) that inherits from ApplicationTool"
8
+ desc "Creates a Tool (in app/mcp/tools) that inherits from ApplicationTool"
9
9
 
10
10
  # The generator takes one argument, e.g. "CalculateSum"
11
11
  argument :name, type: :string, required: true, banner: "ToolName"
12
12
 
13
13
  def create_tool_file
14
- template "tool.rb.erb", "app/tools/#{file_name}.rb"
14
+ template "tool.rb.erb", "app/mcp/tools/#{file_name}.rb"
15
15
  end
16
16
 
17
17
  private
@@ -1,4 +1,5 @@
1
1
  namespace :action_mcp do
2
+ # bin/rails action_mcp:list_tools
2
3
  desc "List all tools with their names and descriptions"
3
4
  task list_tools: :environment do
4
5
  # Ensure Rails eager loads all classes
@@ -7,10 +8,13 @@ namespace :action_mcp do
7
8
  puts "\e[34mACTION MCP TOOLS\e[0m" # Blue
8
9
  puts "\e[34m---------------\e[0m" # Blue
9
10
  ActionMCP::Tool.descendants.each do |tool|
11
+ next if tool.abstract?
10
12
  puts "\e[34m#{tool.capability_name}:\e[0m #{tool.description}" # Blue name
11
13
  end
14
+ puts "\n"
12
15
  end
13
16
 
17
+ # bin/rails action_mcp:list_prompts
14
18
  desc "List all prompts with their names and descriptions"
15
19
  task list_prompts: :environment do
16
20
  # Ensure Rails eager loads all classes
@@ -19,11 +23,29 @@ namespace :action_mcp do
19
23
  puts "\e[32mACTION MCP PROMPTS\e[0m" # Red
20
24
  puts "\e[32m-----------------\e[0m" # Red
21
25
  ActionMCP::Prompt.descendants.each do |prompt|
26
+ next if prompt.abstract?
22
27
  puts "\e[32m#{prompt.capability_name}:\e[0m #{prompt.description}" # Red name
23
28
  end
29
+ puts "\n"
30
+ end
31
+
32
+ # bin/rails action_mcp:list_resources
33
+ desc "List all resources with their names and descriptions"
34
+ task list_resources: :environment do
35
+ # Ensure Rails eager loads all classes
36
+ Rails.application.eager_load!
37
+
38
+ puts "\e[33mACTION MCP RESOURCES\e[0m" # Yellow
39
+ puts "\e[33m--------------------\e[0m" # Yellow
40
+ ActionMCP::ResourceTemplate.descendants.each do |resource|
41
+ next if resource.abstract?
42
+ puts "\e[33m#{resource.capability_name}:\e[0m #{resource.description}" # Yellow name
43
+ end
44
+ puts "\n"
24
45
  end
25
46
 
26
47
  desc "List all tools and prompts with their names and descriptions"
27
- task list: [ :list_tools, :list_prompts ] do
48
+ task list: [ :list_tools, :list_prompts, :list_resources ] do
49
+ # This task lists all tools, prompts, and resources
28
50
  end
29
51
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionmcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-12 00:00:00.000000000 Z
10
+ date: 2025-03-14 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: railties
@@ -139,14 +139,18 @@ files:
139
139
  - lib/action_mcp/registry_base.rb
140
140
  - lib/action_mcp/renderable.rb
141
141
  - lib/action_mcp/resource.rb
142
+ - lib/action_mcp/resource_template.rb
143
+ - lib/action_mcp/resource_templates_registry.rb
142
144
  - lib/action_mcp/server.rb
143
145
  - lib/action_mcp/string_array.rb
146
+ - lib/action_mcp/test_helper.rb
144
147
  - lib/action_mcp/tool.rb
145
148
  - lib/action_mcp/tools_registry.rb
146
149
  - lib/action_mcp/transport.rb
147
150
  - lib/action_mcp/transport/capabilities.rb
148
151
  - lib/action_mcp/transport/messaging.rb
149
152
  - lib/action_mcp/transport/prompts.rb
153
+ - lib/action_mcp/transport/resources.rb
150
154
  - lib/action_mcp/transport/sse_client.rb
151
155
  - lib/action_mcp/transport/stdio_client.rb
152
156
  - lib/action_mcp/transport/tools.rb
@@ -157,8 +161,11 @@ files:
157
161
  - lib/generators/action_mcp/install/install_generator.rb
158
162
  - lib/generators/action_mcp/install/templates/application_prompt.rb
159
163
  - lib/generators/action_mcp/install/templates/application_tool.rb
164
+ - lib/generators/action_mcp/install/templates/mcp_resource_template.rb
160
165
  - lib/generators/action_mcp/prompt/prompt_generator.rb
161
166
  - lib/generators/action_mcp/prompt/templates/prompt.rb.erb
167
+ - lib/generators/action_mcp/resource_template/resource_template_generator.rb
168
+ - lib/generators/action_mcp/resource_template/templates/resource_template.rb.erb
162
169
  - lib/generators/action_mcp/tool/templates/tool.rb.erb
163
170
  - lib/generators/action_mcp/tool/tool_generator.rb
164
171
  - lib/tasks/action_mcp_tasks.rake