rails_mcp_engine 0.1.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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6c501b19758aada1c867684284621f255130d3d031aa39d4df44da9f3a79f93
|
|
4
|
+
data.tar.gz: e07a3da73bf2414648584e8a1eaa05b45e903d64151ec458dfd554c9ae15c0bd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6a4df6b3b995f865c86ed8d648d1629207ad9a603033f4b74ecbfff58c9913550b90588ae71952fbec505b3b4699e1c6247c4b310e7f777a60c3167c32aadb5b
|
|
7
|
+
data.tar.gz: 76372d4ad6346c20b65306dc277e7daba2921089fe808e721258b5b6ecb3bb72ba8d1caf16e57f7be728ad0e77a5131ad0b6cbe22d1adbf86dd2ec64a66ec345
|
data/README.md
CHANGED
|
@@ -99,6 +99,23 @@ Tools::MetaToolService.new.register_tool(
|
|
|
99
99
|
|
|
100
100
|
These hooks are executed around the tool's entrypoint method for both RubyLLM and FastMCP wrappers.
|
|
101
101
|
|
|
102
|
+
## Using Tools in Host Application
|
|
103
|
+
|
|
104
|
+
You can easily fetch the generated RubyLLM tool classes for use in your host application (e.g., when calling an LLM API):
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
# Fetch specific tool classes by name
|
|
108
|
+
tool_classes = Tools::MetaToolService.ruby_llm_tools(['book_meeting', 'calculator'])
|
|
109
|
+
|
|
110
|
+
# Use them with RubyLLM
|
|
111
|
+
response = RubyLLM.chat(
|
|
112
|
+
messages: messages,
|
|
113
|
+
tools: tool_classes,
|
|
114
|
+
model: 'gpt-4o'
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
|
|
102
119
|
## Development
|
|
103
120
|
|
|
104
121
|
After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rails test` to run the tests.
|
|
@@ -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(
|
|
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::
|
|
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(
|
|
172
|
-
|
|
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,28 +44,19 @@ 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
48
|
|
|
55
|
-
|
|
49
|
+
sig { params(tool_names: T::Array[String]).returns(T::Array[T.class_of(Object)]) }
|
|
50
|
+
def self.ruby_llm_tools(tool_names)
|
|
51
|
+
ToolMeta.registry.filter_map do |service_class|
|
|
52
|
+
schema = ToolSchema::Builder.build(service_class)
|
|
53
|
+
next unless tool_names.include?(schema[:name])
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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}" }
|
|
55
|
+
tool_class_name = ToolSchema::RubyLlmFactory.tool_class_name(service_class)
|
|
56
|
+
Tools.const_get(tool_class_name) if Tools.const_defined?(tool_class_name)
|
|
57
|
+
end
|
|
66
58
|
end
|
|
67
59
|
|
|
68
|
-
private
|
|
69
60
|
|
|
70
61
|
sig { params(query: T.nilable(String)).returns(T::Hash[Symbol, T.untyped]) }
|
|
71
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
|
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.
|
|
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
|