actionmodelcontext 2026.11.02.161253
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +307 -0
- data/lib/action_model_context/error.rb +9 -0
- data/lib/action_model_context/error_reporter.rb +30 -0
- data/lib/action_model_context/naming.rb +13 -0
- data/lib/action_model_context/primitive.rb +113 -0
- data/lib/action_model_context/prompt.rb +122 -0
- data/lib/action_model_context/protected_resource_metadata_middleware.rb +28 -0
- data/lib/action_model_context/railtie.rb +35 -0
- data/lib/action_model_context/resource.rb +133 -0
- data/lib/action_model_context/server.rb +166 -0
- data/lib/action_model_context/test_helper.rb +61 -0
- data/lib/action_model_context/tool.rb +196 -0
- data/lib/action_model_context/translation.rb +30 -0
- data/lib/action_model_context/version.rb +3 -0
- data/lib/action_model_context.rb +14 -0
- data/lib/actionmodelcontext.rb +1 -0
- data/lib/generators/action_model_context/argument.rb +57 -0
- data/lib/generators/action_model_context/install/install_generator.rb +31 -0
- data/lib/generators/action_model_context/install/templates/application_prompt.rb.tt +2 -0
- data/lib/generators/action_model_context/install/templates/application_resource.rb.tt +2 -0
- data/lib/generators/action_model_context/install/templates/application_tool.rb.tt +2 -0
- data/lib/generators/action_model_context/install/templates/mcp_controller.rb.tt +8 -0
- data/lib/generators/action_model_context/prompt/prompt_generator.rb +47 -0
- data/lib/generators/action_model_context/prompt/templates/prompt.rb.tt +10 -0
- data/lib/generators/action_model_context/resource/resource_generator.rb +47 -0
- data/lib/generators/action_model_context/resource/templates/resource.rb.tt +8 -0
- data/lib/generators/action_model_context/tool/templates/tool.rb.tt +10 -0
- data/lib/generators/action_model_context/tool/tool_generator.rb +47 -0
- data/lib/generators/rspec/mcp_prompt/mcp_prompt_generator.rb +20 -0
- data/lib/generators/rspec/mcp_prompt/templates/prompt_spec.rb.tt +11 -0
- data/lib/generators/rspec/mcp_resource/mcp_resource_generator.rb +17 -0
- data/lib/generators/rspec/mcp_resource/templates/resource_spec.rb.tt +11 -0
- data/lib/generators/rspec/mcp_tool/mcp_tool_generator.rb +27 -0
- data/lib/generators/rspec/mcp_tool/templates/tool_spec.rb.tt +14 -0
- data/lib/generators/test_unit/mcp_prompt/mcp_prompt_generator.rb +20 -0
- data/lib/generators/test_unit/mcp_prompt/templates/prompt_test.rb.tt +11 -0
- data/lib/generators/test_unit/mcp_resource/mcp_resource_generator.rb +17 -0
- data/lib/generators/test_unit/mcp_resource/templates/resource_test.rb.tt +11 -0
- data/lib/generators/test_unit/mcp_tool/mcp_tool_generator.rb +27 -0
- data/lib/generators/test_unit/mcp_tool/templates/tool_test.rb.tt +14 -0
- metadata +130 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
require 'active_support/core_ext/object/blank'
|
|
3
|
+
|
|
4
|
+
module ActionModelContext
|
|
5
|
+
# Generators of the install, tools, resources and prompts.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
module Generators
|
|
9
|
+
TYPES = {
|
|
10
|
+
'string' => :string, 'text' => :string, 'integer' => :integer,
|
|
11
|
+
'float' => :number, 'decimal' => :number, 'number' => :number,
|
|
12
|
+
'boolean' => :boolean, 'array' => :array, 'object' => :object
|
|
13
|
+
}.freeze
|
|
14
|
+
PLACEHOLDERS = { string: "'example'", integer: '1', number: '1.0', boolean: 'true', array: '[]', object: '{}' }.freeze
|
|
15
|
+
|
|
16
|
+
# An argument read from the command line of a generator.
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
Argument = Data.define(:name, :type, :required) do
|
|
20
|
+
# Parses `name:type:required`, without the type when `typed` is false.
|
|
21
|
+
#
|
|
22
|
+
# @param spec [String] the argument on the command line
|
|
23
|
+
# @param typed [Boolean] whether it may hold a type
|
|
24
|
+
# @return [Argument]
|
|
25
|
+
# @raise [Thor::Error] when the argument is invalid
|
|
26
|
+
def self.parse(spec, typed: true)
|
|
27
|
+
name, *parts = spec.split(':')
|
|
28
|
+
required = parts.delete('required') ? true : false
|
|
29
|
+
raise Thor::Error, "Invalid argument #{spec.inspect}, expected name#{':type' if typed}[:required]" if name.blank? || parts.size > (typed ? 1 : 0)
|
|
30
|
+
|
|
31
|
+
new(name:, type: typed ? resolve_type(parts.first, spec) : nil, required:)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Maps a command line type to a JSON Schema type.
|
|
35
|
+
#
|
|
36
|
+
# @param type [String, nil] the type, `:string` being the default
|
|
37
|
+
# @param spec [String] the argument on the command line, for the error message
|
|
38
|
+
# @return [Symbol]
|
|
39
|
+
# @raise [Thor::Error] when the type is unknown
|
|
40
|
+
def self.resolve_type(type, spec)
|
|
41
|
+
return :string if type.nil?
|
|
42
|
+
|
|
43
|
+
TYPES.fetch(type) { raise Thor::Error, "Unknown type #{type.inspect} in #{spec.inspect}, expected one of: #{TYPES.keys.join(', ')}" }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Gives the keyword of the argument in the generated signature.
|
|
47
|
+
#
|
|
48
|
+
# @return [String]
|
|
49
|
+
def keyword = required ? "#{name}:" : "#{name}: nil"
|
|
50
|
+
|
|
51
|
+
# Gives the argument with an example value, for the generated test.
|
|
52
|
+
#
|
|
53
|
+
# @return [String]
|
|
54
|
+
def placeholder = "#{name}: #{PLACEHOLDERS.fetch(type)}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
|
|
3
|
+
module ActionModelContext
|
|
4
|
+
module Generators
|
|
5
|
+
# Creates the base classes, the controller and the route.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
|
9
|
+
source_root File.expand_path('templates', __dir__)
|
|
10
|
+
|
|
11
|
+
# Creates `ApplicationTool`, `ApplicationResource` and `ApplicationPrompt`.
|
|
12
|
+
#
|
|
13
|
+
# @return [void]
|
|
14
|
+
def create_base_classes
|
|
15
|
+
template 'application_tool.rb', 'app/mcp/tools/application_tool.rb'
|
|
16
|
+
template 'application_resource.rb', 'app/mcp/resources/application_resource.rb'
|
|
17
|
+
template 'application_prompt.rb', 'app/mcp/prompts/application_prompt.rb'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Creates `McpController`.
|
|
21
|
+
#
|
|
22
|
+
# @return [void]
|
|
23
|
+
def create_controller = template('mcp_controller.rb', 'app/controllers/mcp_controller.rb')
|
|
24
|
+
|
|
25
|
+
# Adds the route of the controller.
|
|
26
|
+
#
|
|
27
|
+
# @return [void]
|
|
28
|
+
def add_route = route('resource :mcp, controller: :mcp, only: %i[show create destroy]')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
require 'generators/action_model_context/argument'
|
|
3
|
+
|
|
4
|
+
module ActionModelContext
|
|
5
|
+
module Generators
|
|
6
|
+
# Creates a prompt under `app/mcp/prompts`, and its test.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class PromptGenerator < Rails::Generators::NamedBase
|
|
10
|
+
source_root File.expand_path('templates', __dir__)
|
|
11
|
+
|
|
12
|
+
argument :specs, type: :array, default: [], banner: 'name[:required]'
|
|
13
|
+
|
|
14
|
+
# Creates the prompt.
|
|
15
|
+
#
|
|
16
|
+
# @return [void]
|
|
17
|
+
def create_prompt_file = template('prompt.rb', File.join('app/mcp/prompts', class_path, "#{file_name}_prompt.rb"))
|
|
18
|
+
|
|
19
|
+
hook_for :test_framework, as: :mcp_prompt do |generator|
|
|
20
|
+
invoke generator, [ remove_possible_suffix(name), *specs ]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Drops the suffix from the file name.
|
|
26
|
+
#
|
|
27
|
+
# @return [String]
|
|
28
|
+
def file_name = @_file_name ||= remove_possible_suffix(super)
|
|
29
|
+
|
|
30
|
+
# Drops the `Prompt` suffix from a name.
|
|
31
|
+
#
|
|
32
|
+
# @param name [String] the name
|
|
33
|
+
# @return [String]
|
|
34
|
+
def remove_possible_suffix(name) = name.sub(/_?prompt$/i, '')
|
|
35
|
+
|
|
36
|
+
# Parses the arguments of the command line.
|
|
37
|
+
#
|
|
38
|
+
# @return [Array<Argument>]
|
|
39
|
+
def arguments = @arguments ||= specs.map { |spec| Argument.parse(spec, typed: false) }
|
|
40
|
+
|
|
41
|
+
# Gives the signature of the generated `call`.
|
|
42
|
+
#
|
|
43
|
+
# @return [String]
|
|
44
|
+
def signature = arguments.map(&:keyword).join(', ')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class <%= class_name %>Prompt < ApplicationPrompt
|
|
2
|
+
description '<%= class_name.underscore.tr('/', ' ').humanize %>'
|
|
3
|
+
<% arguments.each do |argument| -%>
|
|
4
|
+
argument :<%= argument.name %><%= ', required: true' if argument.required %>
|
|
5
|
+
<% end -%>
|
|
6
|
+
|
|
7
|
+
def call<%= "(#{signature})" if signature.present? %>
|
|
8
|
+
raise ActionModelContext::Error, '<%= class_name %>Prompt is not implemented yet'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
require 'action_model_context'
|
|
3
|
+
|
|
4
|
+
module ActionModelContext
|
|
5
|
+
module Generators
|
|
6
|
+
# Creates a resource under `app/mcp/resources`, and its test.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class ResourceGenerator < Rails::Generators::NamedBase
|
|
10
|
+
source_root File.expand_path('templates', __dir__)
|
|
11
|
+
|
|
12
|
+
class_option :uri, type: :string, desc: 'URI or URI template, e.g. users://{id}'
|
|
13
|
+
|
|
14
|
+
# Creates the resource.
|
|
15
|
+
#
|
|
16
|
+
# @return [void]
|
|
17
|
+
def create_resource_file = template('resource.rb', File.join('app/mcp/resources', class_path, "#{file_name}_resource.rb"))
|
|
18
|
+
|
|
19
|
+
hook_for :test_framework, as: :mcp_resource do |generator|
|
|
20
|
+
invoke generator, [ remove_possible_suffix(name) ]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Drops the suffix from the file name.
|
|
26
|
+
#
|
|
27
|
+
# @return [String]
|
|
28
|
+
def file_name = @_file_name ||= remove_possible_suffix(super)
|
|
29
|
+
|
|
30
|
+
# Drops the `Resource` suffix from a name.
|
|
31
|
+
#
|
|
32
|
+
# @param name [String] the name
|
|
33
|
+
# @return [String]
|
|
34
|
+
def remove_possible_suffix(name) = name.sub(/_?resource$/i, '')
|
|
35
|
+
|
|
36
|
+
# Gives the URI of the generated resource.
|
|
37
|
+
#
|
|
38
|
+
# @return [String]
|
|
39
|
+
def uri = options[:uri] || "app://#{file_name}"
|
|
40
|
+
|
|
41
|
+
# Gives the signature of the generated `read`.
|
|
42
|
+
#
|
|
43
|
+
# @return [String]
|
|
44
|
+
def signature = uri.scan(ActionModelContext::Resource::URI_VARIABLE).flatten.map { |variable| "#{variable}:" }.join(', ')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class <%= class_name %>Resource < ApplicationResource
|
|
2
|
+
uri '<%= uri %>'
|
|
3
|
+
description '<%= class_name.underscore.tr('/', ' ').humanize %>'
|
|
4
|
+
|
|
5
|
+
def read<%= "(#{signature})" if signature.present? %>
|
|
6
|
+
raise ActionModelContext::Error, '<%= class_name %>Resource is not implemented yet'
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class <%= class_name %>Tool < ApplicationTool
|
|
2
|
+
description '<%= class_name.underscore.tr('/', ' ').humanize %>'
|
|
3
|
+
<% arguments.each do |argument| -%>
|
|
4
|
+
argument :<%= argument.name %>, :<%= argument.type %><%= ', required: true' if argument.required %>
|
|
5
|
+
<% end -%>
|
|
6
|
+
|
|
7
|
+
def call<%= "(#{signature})" if signature.present? %>
|
|
8
|
+
raise ActionModelContext::Error, '<%= class_name %>Tool is not implemented yet'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
require 'generators/action_model_context/argument'
|
|
3
|
+
|
|
4
|
+
module ActionModelContext
|
|
5
|
+
module Generators
|
|
6
|
+
# Creates a tool under `app/mcp/tools`, and its test.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class ToolGenerator < Rails::Generators::NamedBase
|
|
10
|
+
source_root File.expand_path('templates', __dir__)
|
|
11
|
+
|
|
12
|
+
argument :specs, type: :array, default: [], banner: 'name:type[:required]'
|
|
13
|
+
|
|
14
|
+
# Creates the tool.
|
|
15
|
+
#
|
|
16
|
+
# @return [void]
|
|
17
|
+
def create_tool_file = template('tool.rb', File.join('app/mcp/tools', class_path, "#{file_name}_tool.rb"))
|
|
18
|
+
|
|
19
|
+
hook_for :test_framework, as: :mcp_tool do |generator|
|
|
20
|
+
invoke generator, [ remove_possible_suffix(name), *specs ]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Drops the suffix from the file name.
|
|
26
|
+
#
|
|
27
|
+
# @return [String]
|
|
28
|
+
def file_name = @_file_name ||= remove_possible_suffix(super)
|
|
29
|
+
|
|
30
|
+
# Drops the `Tool` suffix from a name.
|
|
31
|
+
#
|
|
32
|
+
# @param name [String] the name
|
|
33
|
+
# @return [String]
|
|
34
|
+
def remove_possible_suffix(name) = name.sub(/_?tool$/i, '')
|
|
35
|
+
|
|
36
|
+
# Parses the arguments of the command line.
|
|
37
|
+
#
|
|
38
|
+
# @return [Array<Argument>]
|
|
39
|
+
def arguments = @arguments ||= specs.map { |spec| Argument.parse(spec) }
|
|
40
|
+
|
|
41
|
+
# Gives the signature of the generated `call`.
|
|
42
|
+
#
|
|
43
|
+
# @return [String]
|
|
44
|
+
def signature = arguments.map(&:keyword).join(', ')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
|
|
3
|
+
# RSpec generators of the specs of the primitives.
|
|
4
|
+
#
|
|
5
|
+
# @api private
|
|
6
|
+
module Rspec
|
|
7
|
+
module Generators
|
|
8
|
+
# Creates the spec of a prompt.
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class McpPromptGenerator < Rails::Generators::NamedBase
|
|
12
|
+
source_root File.expand_path('templates', __dir__)
|
|
13
|
+
|
|
14
|
+
# Creates the spec.
|
|
15
|
+
#
|
|
16
|
+
# @return [void]
|
|
17
|
+
def create_prompt_test = template('prompt_spec.rb', File.join('spec/mcp/prompts', class_path, "#{file_name}_prompt_spec.rb"))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
|
|
3
|
+
module Rspec
|
|
4
|
+
module Generators
|
|
5
|
+
# Creates the spec of a resource.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
class McpResourceGenerator < Rails::Generators::NamedBase
|
|
9
|
+
source_root File.expand_path('templates', __dir__)
|
|
10
|
+
|
|
11
|
+
# Creates the spec.
|
|
12
|
+
#
|
|
13
|
+
# @return [void]
|
|
14
|
+
def create_resource_test = template('resource_spec.rb', File.join('spec/mcp/resources', class_path, "#{file_name}_resource_spec.rb"))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
require 'generators/action_model_context/argument'
|
|
3
|
+
|
|
4
|
+
module Rspec
|
|
5
|
+
module Generators
|
|
6
|
+
# Creates the spec of a tool.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class McpToolGenerator < Rails::Generators::NamedBase
|
|
10
|
+
source_root File.expand_path('templates', __dir__)
|
|
11
|
+
|
|
12
|
+
argument :specs, type: :array, default: [], banner: 'name[:type][:required]'
|
|
13
|
+
|
|
14
|
+
# Creates the spec.
|
|
15
|
+
#
|
|
16
|
+
# @return [void]
|
|
17
|
+
def create_tool_test = template('tool_spec.rb', File.join('spec/mcp/tools', class_path, "#{file_name}_tool_spec.rb"))
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# Gives the required arguments with example values.
|
|
22
|
+
#
|
|
23
|
+
# @return [String]
|
|
24
|
+
def placeholders = specs.map { |spec| ActionModelContext::Generators::Argument.parse(spec) }.select(&:required).map(&:placeholder).join(', ')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'rails_helper'
|
|
2
|
+
require 'action_model_context/test_helper'
|
|
3
|
+
|
|
4
|
+
RSpec.describe <%= class_name %>Tool do
|
|
5
|
+
include ActionModelContext::TestHelper
|
|
6
|
+
|
|
7
|
+
it 'calls the tool' do
|
|
8
|
+
skip 'Fill in the arguments and expectations'
|
|
9
|
+
|
|
10
|
+
result = call_tool(<%= ["#{class_name}Tool", placeholders].reject(&:blank?).join(', ') %>)
|
|
11
|
+
|
|
12
|
+
expect(result).not_to be_error
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
|
|
3
|
+
# Minitest generators of the tests of the primitives.
|
|
4
|
+
#
|
|
5
|
+
# @api private
|
|
6
|
+
module TestUnit
|
|
7
|
+
module Generators
|
|
8
|
+
# Creates the test of a prompt.
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class McpPromptGenerator < Rails::Generators::NamedBase
|
|
12
|
+
source_root File.expand_path('templates', __dir__)
|
|
13
|
+
|
|
14
|
+
# Creates the test.
|
|
15
|
+
#
|
|
16
|
+
# @return [void]
|
|
17
|
+
def create_prompt_test = template('prompt_test.rb', File.join('test/mcp/prompts', class_path, "#{file_name}_prompt_test.rb"))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>PromptTest < ActiveSupport::TestCase
|
|
4
|
+
test 'builds the prompt' do
|
|
5
|
+
skip 'Fill in the arguments and expectations'
|
|
6
|
+
|
|
7
|
+
prompt = <%= class_name %>Prompt.new(context: {})
|
|
8
|
+
|
|
9
|
+
assert_predicate prompt.call, :present?
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
|
|
3
|
+
module TestUnit
|
|
4
|
+
module Generators
|
|
5
|
+
# Creates the test of a resource.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
class McpResourceGenerator < Rails::Generators::NamedBase
|
|
9
|
+
source_root File.expand_path('templates', __dir__)
|
|
10
|
+
|
|
11
|
+
# Creates the test.
|
|
12
|
+
#
|
|
13
|
+
# @return [void]
|
|
14
|
+
def create_resource_test = template('resource_test.rb', File.join('test/mcp/resources', class_path, "#{file_name}_resource_test.rb"))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>ResourceTest < ActiveSupport::TestCase
|
|
4
|
+
test 'reads the resource' do
|
|
5
|
+
skip 'Fill in the parameters and expectations'
|
|
6
|
+
|
|
7
|
+
resource = <%= class_name %>Resource.new(context: {})
|
|
8
|
+
|
|
9
|
+
assert_predicate resource.read, :present?
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'rails/generators'
|
|
2
|
+
require 'generators/action_model_context/argument'
|
|
3
|
+
|
|
4
|
+
module TestUnit
|
|
5
|
+
module Generators
|
|
6
|
+
# Creates the test of a tool.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class McpToolGenerator < Rails::Generators::NamedBase
|
|
10
|
+
source_root File.expand_path('templates', __dir__)
|
|
11
|
+
|
|
12
|
+
argument :specs, type: :array, default: [], banner: 'name[:type][:required]'
|
|
13
|
+
|
|
14
|
+
# Creates the test.
|
|
15
|
+
#
|
|
16
|
+
# @return [void]
|
|
17
|
+
def create_tool_test = template('tool_test.rb', File.join('test/mcp/tools', class_path, "#{file_name}_tool_test.rb"))
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# Gives the required arguments with example values.
|
|
22
|
+
#
|
|
23
|
+
# @return [String]
|
|
24
|
+
def placeholders = specs.map { |spec| ActionModelContext::Generators::Argument.parse(spec) }.select(&:required).map(&:placeholder).join(', ')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'action_model_context/test_helper'
|
|
3
|
+
|
|
4
|
+
class <%= class_name %>ToolTest < ActiveSupport::TestCase
|
|
5
|
+
include ActionModelContext::TestHelper
|
|
6
|
+
|
|
7
|
+
test 'calls the tool' do
|
|
8
|
+
skip 'Fill in the arguments and expectations'
|
|
9
|
+
|
|
10
|
+
result = call_tool(<%= ["#{class_name}Tool", placeholders].reject(&:blank?).join(', ') %>)
|
|
11
|
+
|
|
12
|
+
assert_not result.error?
|
|
13
|
+
end
|
|
14
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: actionmodelcontext
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 2026.11.02.161253
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jonathan PHILIPPE
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: mcp
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.8'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.8'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: railties
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '7.2'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '7.2'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: actionpack
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '7.2'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '7.2'
|
|
54
|
+
description: Tools, resources and prompts as plain Ruby classes under app/mcp, discovered
|
|
55
|
+
by Zeitwerk and served by a controller concern over the Streamable HTTP transport,
|
|
56
|
+
with I18n, rescue_from, OAuth, generators and a test helper.
|
|
57
|
+
email:
|
|
58
|
+
- jphilippe@fluence.eu
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- LICENSE.txt
|
|
64
|
+
- README.md
|
|
65
|
+
- lib/action_model_context.rb
|
|
66
|
+
- lib/action_model_context/error.rb
|
|
67
|
+
- lib/action_model_context/error_reporter.rb
|
|
68
|
+
- lib/action_model_context/naming.rb
|
|
69
|
+
- lib/action_model_context/primitive.rb
|
|
70
|
+
- lib/action_model_context/prompt.rb
|
|
71
|
+
- lib/action_model_context/protected_resource_metadata_middleware.rb
|
|
72
|
+
- lib/action_model_context/railtie.rb
|
|
73
|
+
- lib/action_model_context/resource.rb
|
|
74
|
+
- lib/action_model_context/server.rb
|
|
75
|
+
- lib/action_model_context/test_helper.rb
|
|
76
|
+
- lib/action_model_context/tool.rb
|
|
77
|
+
- lib/action_model_context/translation.rb
|
|
78
|
+
- lib/action_model_context/version.rb
|
|
79
|
+
- lib/actionmodelcontext.rb
|
|
80
|
+
- lib/generators/action_model_context/argument.rb
|
|
81
|
+
- lib/generators/action_model_context/install/install_generator.rb
|
|
82
|
+
- lib/generators/action_model_context/install/templates/application_prompt.rb.tt
|
|
83
|
+
- lib/generators/action_model_context/install/templates/application_resource.rb.tt
|
|
84
|
+
- lib/generators/action_model_context/install/templates/application_tool.rb.tt
|
|
85
|
+
- lib/generators/action_model_context/install/templates/mcp_controller.rb.tt
|
|
86
|
+
- lib/generators/action_model_context/prompt/prompt_generator.rb
|
|
87
|
+
- lib/generators/action_model_context/prompt/templates/prompt.rb.tt
|
|
88
|
+
- lib/generators/action_model_context/resource/resource_generator.rb
|
|
89
|
+
- lib/generators/action_model_context/resource/templates/resource.rb.tt
|
|
90
|
+
- lib/generators/action_model_context/tool/templates/tool.rb.tt
|
|
91
|
+
- lib/generators/action_model_context/tool/tool_generator.rb
|
|
92
|
+
- lib/generators/rspec/mcp_prompt/mcp_prompt_generator.rb
|
|
93
|
+
- lib/generators/rspec/mcp_prompt/templates/prompt_spec.rb.tt
|
|
94
|
+
- lib/generators/rspec/mcp_resource/mcp_resource_generator.rb
|
|
95
|
+
- lib/generators/rspec/mcp_resource/templates/resource_spec.rb.tt
|
|
96
|
+
- lib/generators/rspec/mcp_tool/mcp_tool_generator.rb
|
|
97
|
+
- lib/generators/rspec/mcp_tool/templates/tool_spec.rb.tt
|
|
98
|
+
- lib/generators/test_unit/mcp_prompt/mcp_prompt_generator.rb
|
|
99
|
+
- lib/generators/test_unit/mcp_prompt/templates/prompt_test.rb.tt
|
|
100
|
+
- lib/generators/test_unit/mcp_resource/mcp_resource_generator.rb
|
|
101
|
+
- lib/generators/test_unit/mcp_resource/templates/resource_test.rb.tt
|
|
102
|
+
- lib/generators/test_unit/mcp_tool/mcp_tool_generator.rb
|
|
103
|
+
- lib/generators/test_unit/mcp_tool/templates/tool_test.rb.tt
|
|
104
|
+
homepage: https://github.com/fluence-eu/actionmodelcontext
|
|
105
|
+
licenses:
|
|
106
|
+
- MIT
|
|
107
|
+
metadata:
|
|
108
|
+
homepage_uri: https://github.com/fluence-eu/actionmodelcontext
|
|
109
|
+
source_code_uri: https://github.com/fluence-eu/actionmodelcontext
|
|
110
|
+
changelog_uri: https://github.com/fluence-eu/actionmodelcontext/blob/main/CHANGELOG.md
|
|
111
|
+
github_repo: https://github.com/fluence-eu/actionmodelcontext
|
|
112
|
+
rubygems_mfa_required: 'true'
|
|
113
|
+
rdoc_options: []
|
|
114
|
+
require_paths:
|
|
115
|
+
- lib
|
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - ">="
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: '3.3'
|
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - ">="
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '0'
|
|
126
|
+
requirements: []
|
|
127
|
+
rubygems_version: 4.0.20
|
|
128
|
+
specification_version: 4
|
|
129
|
+
summary: Rails-flavoured MCP server on top of the official mcp SDK
|
|
130
|
+
test_files: []
|