rails_mcp_engine 0.2.0 → 0.3.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: 46864199e9aea2b6d1e7b9b9c065109f3f70a012c35cc68a632a8fe39fd1ff06
4
- data.tar.gz: a3cbbe5460b4c2d36e84a1d25fa3481e82ab678d1cd12dfef083819db352feb4
3
+ metadata.gz: f6c501b19758aada1c867684284621f255130d3d031aa39d4df44da9f3a79f93
4
+ data.tar.gz: e07a3da73bf2414648584e8a1eaa05b45e903d64151ec458dfd554c9ae15c0bd
5
5
  SHA512:
6
- metadata.gz: a7f7134090f0de5dfb89878baf43ae74e6d5c0d9b9020744cdf4673f49e3daed9e0128e6d80c33d3e6fda0cd12af6b4e43e99307d6e44b817a88cf28e1952487
7
- data.tar.gz: c7eb6c1be9166b14e06341b5ab10a097345fceb88790fceb432c20e09d0c3d707066f9d756e0e30aaa411e1f7d48d0d75f0e7b572ddc1d2cad897ec7987056c5
6
+ metadata.gz: 6a4df6b3b995f865c86ed8d648d1629207ad9a603033f4b74ecbfff58c9913550b90588ae71952fbec505b3b4699e1c6247c4b310e7f777a60c3167c32aadb5b
7
+ data.tar.gz: 76372d4ad6346c20b65306dc277e7daba2921089fe808e721258b5b6ecb3bb72ba8d1caf16e57f7be728ad0e77a5131ad0b6cbe22d1adbf86dd2ec64a66ec345
@@ -50,7 +50,7 @@ module RailsMcpEngine
50
50
  result = if schema.nil?
51
51
  { error: "Tool not found: #{tool_name}" }
52
52
  else
53
- delete_tool_from_registry(schema[:service_class])
53
+ delete_tool_from_registry(tool_name)
54
54
  end
55
55
 
56
56
  flash[:register_result] = result
@@ -139,8 +139,9 @@ module RailsMcpEngine
139
139
  # The engine.rb defines ApplicationTool.
140
140
 
141
141
  # Re-using the logic from ManualController but adapting for Engine.
142
- ::Tools::MetaToolService.new.register_tool(
142
+ ::Tools::MetaToolWriteService.new.register_tool(
143
143
  class_name,
144
+ source: source,
144
145
  before_call: ->(args) { Rails.logger.info(" [MCP] Request #{class_name}: #{args.inspect}") },
145
146
  after_call: ->(result) { Rails.logger.info(" [MCP] Response #{class_name}: #{result.inspect}") }
146
147
  )
@@ -168,14 +169,8 @@ module RailsMcpEngine
168
169
  { error: e.message }
169
170
  end
170
171
 
171
- def delete_tool_from_registry(service_class)
172
- ToolMeta.registry.delete(service_class)
173
-
174
- # Also remove the RubyLLM tool class constant
175
- tool_constant = ToolSchema::RubyLlmFactory.tool_class_name(service_class)
176
- ::Tools.send(:remove_const, tool_constant) if ::Tools.const_defined?(tool_constant, false)
177
-
178
- { success: 'Tool deleted successfully' }
172
+ def delete_tool_from_registry(tool_name)
173
+ ::Tools::MetaToolWriteService.new.delete_tool(tool_name)
179
174
  rescue StandardError => e
180
175
  { error: e.message }
181
176
  end
@@ -44,26 +44,7 @@ module Tools
44
44
  end
45
45
  end
46
46
 
47
- sig { params(class_name: T.nilable(String), before_call: T.nilable(Proc), after_call: T.nilable(Proc)).returns(T::Hash[Symbol, T.untyped]) }
48
- def register_tool(class_name, before_call: nil, after_call: nil)
49
- return { error: 'class_name is required for register' } if class_name.nil? || class_name.empty?
50
47
 
51
- service_class = constantize(class_name)
52
- return { error: "Could not find #{class_name}" } if service_class.nil?
53
- return { error: "#{class_name} must extend ToolMeta" } unless service_class.respond_to?(:tool_metadata)
54
-
55
- ToolMeta.registry << service_class unless ToolMeta.registry.include?(service_class)
56
-
57
- schema = ToolSchema::Builder.build(service_class)
58
- ToolSchema::RubyLlmFactory.build(service_class, schema, before_call: before_call, after_call: after_call)
59
- ToolSchema::FastMcpFactory.build(service_class, schema, before_call: before_call, after_call: after_call)
60
-
61
- { status: 'registered', tool: summary_payload(schema) }
62
- rescue ToolMeta::MissingSignatureError => e
63
- { error: e.message }
64
- rescue NameError => e
65
- { error: "Could not find #{class_name}: #{e.message}" }
66
- end
67
48
 
68
49
  sig { params(tool_names: T::Array[String]).returns(T::Array[T.class_of(Object)]) }
69
50
  def self.ruby_llm_tools(tool_names)
@@ -76,7 +57,6 @@ module Tools
76
57
  end
77
58
  end
78
59
 
79
- private
80
60
 
81
61
  sig { params(query: T.nilable(String)).returns(T::Hash[Symbol, T.untyped]) }
82
62
  def search_tools(query)
@@ -0,0 +1,72 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'sorbet-runtime'
5
+ require 'tool_meta'
6
+ require 'tool_schema/builder'
7
+ require 'tool_schema/ruby_llm_factory'
8
+ require 'tool_schema/fast_mcp_factory'
9
+
10
+ module Tools
11
+ class MetaToolWriteService
12
+ extend T::Sig
13
+
14
+ sig { params(class_name: T.nilable(String), source: T.nilable(String), before_call: T.nilable(Proc), after_call: T.nilable(Proc)).returns(T::Hash[Symbol, T.untyped]) }
15
+ def register_tool(class_name, source: nil, before_call: nil, after_call: nil)
16
+ return { error: 'class_name is required for register' } if class_name.nil? || class_name.empty?
17
+
18
+ # If source is provided, evaluate it first
19
+ if source
20
+ begin
21
+ Object.class_eval(source)
22
+ rescue StandardError => e
23
+ return { error: "Failed to evaluate source: #{e.message}" }
24
+ end
25
+ end
26
+
27
+ begin
28
+ service_class = meta_service.constantize(class_name)
29
+ rescue NameError
30
+ return { error: "Could not find #{class_name}" }
31
+ end
32
+
33
+ return { error: "#{class_name} must extend ToolMeta" } unless service_class.respond_to?(:tool_metadata)
34
+
35
+ ToolMeta.registry << service_class unless ToolMeta.registry.include?(service_class)
36
+
37
+ schema = ToolSchema::Builder.build(service_class)
38
+ ToolSchema::RubyLlmFactory.build(service_class, schema, before_call: before_call, after_call: after_call)
39
+ ToolSchema::FastMcpFactory.build(service_class, schema, before_call: before_call, after_call: after_call)
40
+
41
+ { status: 'registered', tool: meta_service.summary_payload(schema) }
42
+ rescue ToolMeta::MissingSignatureError => e
43
+ { error: e.message }
44
+ rescue NameError => e
45
+ { error: "Could not find #{class_name}: #{e.message}" }
46
+ end
47
+
48
+ sig { params(tool_name: String).returns(T::Hash[Symbol, T.untyped]) }
49
+ def delete_tool(tool_name)
50
+ schema = meta_service.find_schema(tool_name)
51
+ return { error: "Tool not found: #{tool_name}" } unless schema
52
+
53
+ service_class = schema[:service_class]
54
+ ToolMeta.registry.delete(service_class)
55
+
56
+ tool_constant = ToolSchema::RubyLlmFactory.tool_class_name(service_class)
57
+ Tools.send(:remove_const, tool_constant) if Tools.const_defined?(tool_constant, false)
58
+ fast_mcp_constant = ToolSchema::FastMcpFactory.tool_class_name(service_class)
59
+ Mcp.send(:remove_const, fast_mcp_constant) if Mcp.const_defined?(fast_mcp_constant, false)
60
+
61
+ { success: 'Tool deleted successfully' }
62
+ rescue StandardError => e
63
+ { error: e.message }
64
+ end
65
+
66
+ private
67
+
68
+ def meta_service
69
+ @meta_service ||= Tools::MetaToolService.new
70
+ end
71
+ end
72
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsMcpEngine
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_mcp_engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soonoh Jung
@@ -92,6 +92,7 @@ files:
92
92
  - app/lib/tool_schema/ruby_llm_factory.rb
93
93
  - app/lib/tool_schema/sorbet_type_mapper.rb
94
94
  - app/services/tools/meta_tool_service.rb
95
+ - app/services/tools/meta_tool_write_service.rb
95
96
  - app/views/rails_mcp_engine/chat/show.html.erb
96
97
  - app/views/rails_mcp_engine/playground/show.html.erb
97
98
  - config/routes.rb