ruby_llm-mcp 0.6.4 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bec47480f606a8d28f8a79032e392e331dcdcef565cd708e6dcc855f0a31445
4
- data.tar.gz: ac65a564ce2bd4d39e4e4fdcff02da19cbd7573186924e938fadb2d9c2f523db
3
+ metadata.gz: b40509c28a60e1ae01ed83d47503d76f30d5f3bf9852ef4c30ca033d6ea149bc
4
+ data.tar.gz: f3393dc6423c6bcda143cb6b22e7042434ca1d7d385cc2adf97ba925efff6912
5
5
  SHA512:
6
- metadata.gz: 7e9394538e772a3169fe424854cbf57aee643953be4e44f805c82f0f81c61189a06d755e8a14b4e530183e414e9a4b3000912a577d3eca95da236c124b9a54dc
7
- data.tar.gz: a6eb704e2aaac9386e54cb178dd136bc19510a2159adc63125d50f5a96c9473de9416e33755059f9578ab609d34fe3fbe8c8d7f8e970900380b93770793376a3
6
+ metadata.gz: 86e6dd3009df055ad4fa6d088b6b1d9fb87cc8c6244b1436e4ef96ef1cbd822c321c779bd518557544a6c457d19270bf1a57c874cb6d80543141ecac7c370754
7
+ data.tar.gz: 5a76f9b2aaf2d3db38d97c9d66341458e25f3cdd89fdaf1768095a1bba20257154e128c9b60245f1f09f3e6f09b967daacd70748008c4edf90c3e836546bd6d6
@@ -11,9 +11,6 @@ RubyLLM::MCP.configure do |config|
11
11
  # Pool timeout in seconds
12
12
  config.pool_timeout = 5
13
13
 
14
- # Enable complex parameter support for various providers
15
- config.support_complex_parameters!
16
-
17
14
  # Path to MCPs configuration file
18
15
  config.config_path = Rails.root.join("config", "mcps.yml")
19
16
 
@@ -31,7 +28,7 @@ RubyLLM::MCP.configure do |config|
31
28
  # Set preferred model for sampling
32
29
  # config.sampling.preferred_model do
33
30
  # # Return the preferred model name
34
- # "claude-3-5-sonnet-20240620"
31
+ # "claude-sonnet-4"
35
32
  # end
36
33
 
37
34
  # Set a guard for sampling
@@ -111,6 +111,7 @@ module RubyLLM
111
111
  end
112
112
 
113
113
  def support_complex_parameters!
114
+ warn "[DEPRECATION] config.support_complex_parameters! is no longer needed and will be removed in version 0.8.0"
114
115
  return if @has_support_complex_parameters
115
116
 
116
117
  @has_support_complex_parameters = true
@@ -7,13 +7,21 @@ module RubyLLM
7
7
 
8
8
  def initialize(text: nil, attachments: nil) # rubocop:disable Lint/MissingSuper
9
9
  @text = text
10
- @attachments = attachments || []
10
+ @attachments = []
11
+
12
+ # Handle MCP::Attachment objects directly without processing
13
+ if attachments.is_a?(Array) && attachments.all? { |a| a.is_a?(MCP::Attachment) }
14
+ @attachments = attachments
15
+ elsif attachments
16
+ # Let parent class process other types of attachments
17
+ process_attachments(attachments)
18
+ end
11
19
  end
12
20
 
13
21
  # This is a workaround to allow the content object to be passed as the tool call
14
22
  # to return audio or image attachments.
15
23
  def to_s
16
- attachments.empty? ? text : self
24
+ text.to_s
17
25
  end
18
26
  end
19
27
  end
@@ -51,7 +51,7 @@ module RubyLLM
51
51
  end
52
52
 
53
53
  def include(chat, **args)
54
- message = Message.new(
54
+ message = RubyLLM::Message.new(
55
55
  role: "user",
56
56
  content: to_content(**args)
57
57
  )
@@ -25,7 +25,7 @@ module RubyLLM
25
25
  end
26
26
 
27
27
  class Tool < RubyLLM::Tool
28
- attr_reader :name, :title, :description, :parameters, :coordinator, :tool_response, :with_prefix
28
+ attr_reader :name, :title, :description, :coordinator, :tool_response, :with_prefix
29
29
 
30
30
  def initialize(coordinator, tool_response, with_prefix: false)
31
31
  super()
@@ -35,18 +35,23 @@ module RubyLLM
35
35
  @name = format_name(tool_response["name"])
36
36
  @mcp_name = tool_response["name"]
37
37
  @description = tool_response["description"].to_s
38
- @parameters = create_parameters(tool_response["inputSchema"])
39
38
 
40
39
  @input_schema = tool_response["inputSchema"]
41
40
  @output_schema = tool_response["outputSchema"]
42
41
 
43
42
  @annotations = tool_response["annotations"] ? Annotation.new(tool_response["annotations"]) : nil
43
+
44
+ @normalized_input_schema = normalize_if_invalid(@input_schema)
44
45
  end
45
46
 
46
47
  def display_name
47
48
  "#{@coordinator.name}: #{@name}"
48
49
  end
49
50
 
51
+ def params_schema
52
+ @normalized_input_schema
53
+ end
54
+
50
55
  def execute(**params)
51
56
  result = @coordinator.execute_tool(
52
57
  name: @mcp_name,
@@ -83,7 +88,7 @@ module RubyLLM
83
88
  {
84
89
  name: @name,
85
90
  description: @description,
86
- parameters: @parameters.to_h,
91
+ params_schema: @@normalized_input_schema,
87
92
  annotations: @annotations&.to_h
88
93
  }
89
94
  end
@@ -92,76 +97,6 @@ module RubyLLM
92
97
 
93
98
  private
94
99
 
95
- def create_parameters(schema)
96
- params = {}
97
- return params if schema["properties"].nil?
98
-
99
- schema["properties"].each_key do |key|
100
- param_data = schema.dig("properties", key)
101
- param_data = expand_shorthand_type_to_anyof(param_data)
102
-
103
- param = if param_data.key?("oneOf") || param_data.key?("anyOf") || param_data.key?("allOf")
104
- process_union_parameter(key, param_data)
105
- else
106
- process_parameter(key, param_data)
107
- end
108
-
109
- params[key] = param
110
- end
111
-
112
- params
113
- end
114
-
115
- def process_union_parameter(key, param_data)
116
- union_type = param_data.keys.first
117
- param = RubyLLM::MCP::Parameter.new(
118
- key,
119
- type: :union,
120
- title: param_data["title"],
121
- desc: param_data["description"],
122
- union_type: union_type
123
- )
124
-
125
- param.properties = param_data[union_type].map do |value|
126
- expanded_value = expand_shorthand_type_to_anyof(value)
127
- if expanded_value.key?("anyOf")
128
- process_union_parameter(key, expanded_value)
129
- else
130
- process_parameter(key, value, lifted_type: param_data["type"])
131
- end
132
- end.compact
133
-
134
- param
135
- end
136
-
137
- def process_parameter(key, param_data, lifted_type: nil)
138
- param = RubyLLM::MCP::Parameter.new(
139
- key,
140
- type: param_data["type"] || lifted_type || "string",
141
- title: param_data["title"],
142
- desc: param_data["description"],
143
- required: param_data["required"],
144
- default: param_data["default"]
145
- )
146
-
147
- if param.type == :array
148
- items = param_data["items"]
149
- param.items = items
150
- if items.key?("properties")
151
- param.properties = create_parameters(items)
152
- end
153
- if items.key?("enum")
154
- param.enum = items["enum"]
155
- end
156
- elsif param.type == :object
157
- if param_data.key?("properties")
158
- param.properties = create_parameters(param_data)
159
- end
160
- end
161
-
162
- param
163
- end
164
-
165
100
  def create_content_for_message(content)
166
101
  case content["type"]
167
102
  when "text"
@@ -205,19 +140,88 @@ module RubyLLM
205
140
  end
206
141
  end
207
142
 
208
- # Expands shorthand type arrays into explicit anyOf unions
209
- # Converts { "type": ["string", "number"] } into { "anyOf": [{"type": "string"}, {"type": "number"}] }
210
- # This keeps $ref references clean and provides a consistent structure for union types
211
- #
212
- # @param param_data [Hash] The parameter data that may contain a shorthand type array
213
- # @return [Hash] The expanded parameter data with anyOf, or the original if not a shorthand
214
- def expand_shorthand_type_to_anyof(param_data)
215
- type = param_data["type"]
216
- return param_data unless type.is_a?(Array)
143
+ def normalize_schema(schema)
144
+ return schema if schema.nil?
217
145
 
218
- {
219
- "anyOf" => type.map { |t| { "type" => t } }
220
- }.merge(param_data.except("type"))
146
+ case schema
147
+ when Hash
148
+ normalize_hash_schema(schema)
149
+ when Array
150
+ normalize_array_schema(schema)
151
+ else
152
+ schema
153
+ end
154
+ end
155
+
156
+ def normalize_hash_schema(schema)
157
+ normalized = schema.transform_values { |value| normalize_schema_value(value) }
158
+ ensure_object_properties(normalized)
159
+ normalized
160
+ end
161
+
162
+ def normalize_array_schema(schema)
163
+ schema.map { |item| normalize_schema_value(item) }
164
+ end
165
+
166
+ def normalize_schema_value(value)
167
+ case value
168
+ when Hash
169
+ normalize_schema(value)
170
+ when Array
171
+ normalize_array_schema(value)
172
+ else
173
+ value
174
+ end
175
+ end
176
+
177
+ def ensure_object_properties(schema)
178
+ if schema["type"] == "object" && !schema.key?("properties")
179
+ schema["properties"] = {}
180
+ end
181
+ end
182
+
183
+ def normalize_if_invalid(schema)
184
+ return schema if schema.nil?
185
+
186
+ if valid_schema?(schema)
187
+ schema
188
+ else
189
+ normalize_schema(schema)
190
+ end
191
+ end
192
+
193
+ def valid_schema?(schema)
194
+ return true if schema.nil?
195
+
196
+ case schema
197
+ when Hash
198
+ valid_hash_schema?(schema)
199
+ when Array
200
+ schema.all? { |item| valid_schema?(item) }
201
+ else
202
+ true
203
+ end
204
+ end
205
+
206
+ def valid_hash_schema?(schema)
207
+ # Check if this level has missing properties for object type
208
+ if schema["type"] == "object" && !schema.key?("properties")
209
+ return false
210
+ end
211
+
212
+ # Recursively check nested schemas
213
+ schema.each_value do |value|
214
+ return false unless valid_schema?(value)
215
+ end
216
+
217
+ begin
218
+ JSON::Validator.validate!(schema, {})
219
+ true
220
+ rescue JSON::Schema::SchemaError
221
+ false
222
+ rescue JSON::Schema::ValidationError
223
+ true
224
+ end
221
225
  end
222
226
  end
223
227
  end
@@ -78,51 +78,23 @@ module RubyLLM
78
78
  @running = true
79
79
  end
80
80
 
81
- def close # rubocop:disable Metrics/MethodLength
81
+ def close
82
82
  @running = false
83
83
 
84
- begin
85
- @stdin&.close
86
- rescue StandardError
87
- nil
88
- end
89
-
90
- begin
91
- @wait_thread&.join(1)
92
- rescue StandardError
84
+ [@stdin, @stdout, @stderr].each do |stream|
85
+ stream&.close
86
+ rescue IOError, Errno::EBADF
93
87
  nil
94
88
  end
95
89
 
96
- begin
97
- @stdout&.close
98
- rescue StandardError
99
- nil
100
- end
101
-
102
- begin
103
- @stderr&.close
90
+ [@wait_thread, @reader_thread, @stderr_thread].each do |thread|
91
+ thread&.join(1)
104
92
  rescue StandardError
105
93
  nil
106
94
  end
107
95
 
108
- begin
109
- @reader_thread&.join(1)
110
- rescue StandardError
111
- nil
112
- end
113
-
114
- begin
115
- @stderr_thread&.join(1)
116
- rescue StandardError
117
- nil
118
- end
119
-
120
- @stdin = nil
121
- @stdout = nil
122
- @stderr = nil
123
- @wait_thread = nil
124
- @reader_thread = nil
125
- @stderr_thread = nil
96
+ @stdin = @stdout = @stderr = nil
97
+ @wait_thread = @reader_thread = @stderr_thread = nil
126
98
  end
127
99
 
128
100
  def set_protocol_version(version)
@@ -151,58 +123,88 @@ module RubyLLM
151
123
 
152
124
  def start_reader_thread
153
125
  @reader_thread = Thread.new do
154
- while @running
155
- begin
156
- if @stdout.closed? || @wait_thread.nil? || !@wait_thread.alive?
157
- sleep 1
158
- restart_process if @running
159
- next
160
- end
161
-
162
- line = @stdout.gets
163
- next unless line && !line.strip.empty?
164
-
165
- process_response(line.strip)
166
- rescue IOError, Errno::EPIPE => e
167
- RubyLLM::MCP.logger.error "Reader error: #{e.message}. Restarting in 1 second..."
168
- sleep 1
169
- restart_process if @running
170
- rescue StandardError => e
171
- RubyLLM::MCP.logger.error "Error in reader thread: #{e.message}, #{e.backtrace.join("\n")}"
172
- sleep 1
173
- end
174
- end
126
+ read_stdout_loop
175
127
  end
176
128
 
177
129
  @reader_thread.abort_on_exception = true
178
130
  end
179
131
 
132
+ def read_stdout_loop
133
+ while @running
134
+ begin
135
+ handle_stdout_read
136
+ rescue IOError, Errno::EPIPE => e
137
+ handle_stream_error(e, "Reader")
138
+ break unless @running
139
+ rescue StandardError => e
140
+ RubyLLM::MCP.logger.error "Error in reader thread: #{e.message}, #{e.backtrace.join("\n")}"
141
+ sleep 1
142
+ end
143
+ end
144
+ end
145
+
146
+ def handle_stdout_read
147
+ if @stdout.closed? || @wait_thread.nil? || !@wait_thread.alive?
148
+ if @running
149
+ sleep 1
150
+ restart_process
151
+ end
152
+ return
153
+ end
154
+
155
+ line = @stdout.gets
156
+ return unless line && !line.strip.empty?
157
+
158
+ process_response(line.strip)
159
+ end
160
+
161
+ def handle_stream_error(error, stream_name)
162
+ # Check @running to distinguish graceful shutdown from unexpected errors.
163
+ # During shutdown, streams are closed intentionally and shouldn't trigger restarts.
164
+ if @running
165
+ RubyLLM::MCP.logger.error "#{stream_name} error: #{error.message}. Restarting in 1 second..."
166
+ sleep 1
167
+ restart_process
168
+ else
169
+ # Graceful shutdown in progress
170
+ RubyLLM::MCP.logger.debug "#{stream_name} thread exiting during shutdown"
171
+ end
172
+ end
173
+
180
174
  def start_stderr_thread
181
175
  @stderr_thread = Thread.new do
182
- while @running
183
- begin
184
- if @stderr.closed? || @wait_thread.nil? || !@wait_thread.alive?
185
- sleep 1
186
- next
187
- end
188
-
189
- line = @stderr.gets
190
- next unless line && !line.strip.empty?
191
-
192
- RubyLLM::MCP.logger.info(line.strip)
193
- rescue IOError, Errno::EPIPE => e
194
- RubyLLM::MCP.logger.error "Stderr reader error: #{e.message}"
195
- sleep 1
196
- rescue StandardError => e
197
- RubyLLM::MCP.logger.error "Error in stderr thread: #{e.message}"
198
- sleep 1
199
- end
200
- end
176
+ read_stderr_loop
201
177
  end
202
178
 
203
179
  @stderr_thread.abort_on_exception = true
204
180
  end
205
181
 
182
+ def read_stderr_loop
183
+ while @running
184
+ begin
185
+ handle_stderr_read
186
+ rescue IOError, Errno::EPIPE => e
187
+ handle_stream_error(e, "Stderr reader")
188
+ break unless @running
189
+ rescue StandardError => e
190
+ RubyLLM::MCP.logger.error "Error in stderr thread: #{e.message}"
191
+ sleep 1
192
+ end
193
+ end
194
+ end
195
+
196
+ def handle_stderr_read
197
+ if @stderr.closed? || @wait_thread.nil? || !@wait_thread.alive?
198
+ sleep 1
199
+ return
200
+ end
201
+
202
+ line = @stderr.gets
203
+ return unless line && !line.strip.empty?
204
+
205
+ RubyLLM::MCP.logger.info(line.strip)
206
+ end
207
+
206
208
  def process_response(line)
207
209
  response = JSON.parse(line)
208
210
  request_id = response["id"]&.to_s
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyLLM
4
4
  module MCP
5
- VERSION = "0.6.4"
5
+ VERSION = "0.7.1"
6
6
  end
7
7
  end
data/lib/ruby_llm/mcp.rb CHANGED
@@ -62,9 +62,9 @@ module RubyLLM
62
62
  end
63
63
 
64
64
  def support_complex_parameters!
65
- require_relative "mcp/providers/openai/complex_parameter_support"
66
- require_relative "mcp/providers/anthropic/complex_parameter_support"
67
- require_relative "mcp/providers/gemini/complex_parameter_support"
65
+ warn "[DEPRECATION] RubyLLM::MCP.support_complex_parameters! is no longer needed " \
66
+ "and will be removed in version 0.8.0"
67
+ # No-op: Complex parameters are now supported by default
68
68
  end
69
69
 
70
70
  def configure
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm-mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Vice
@@ -43,14 +43,14 @@ dependencies:
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '1.3'
46
+ version: '1.9'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '1.3'
53
+ version: '1.9'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: zeitwerk
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -97,13 +97,9 @@ files:
97
97
  - lib/ruby_llm/mcp/notifications/cancelled.rb
98
98
  - lib/ruby_llm/mcp/notifications/initialize.rb
99
99
  - lib/ruby_llm/mcp/notifications/roots_list_change.rb
100
- - lib/ruby_llm/mcp/parameter.rb
101
100
  - lib/ruby_llm/mcp/progress.rb
102
101
  - lib/ruby_llm/mcp/prompt.rb
103
102
  - lib/ruby_llm/mcp/protocol.rb
104
- - lib/ruby_llm/mcp/providers/anthropic/complex_parameter_support.rb
105
- - lib/ruby_llm/mcp/providers/gemini/complex_parameter_support.rb
106
- - lib/ruby_llm/mcp/providers/openai/complex_parameter_support.rb
107
103
  - lib/ruby_llm/mcp/railtie.rb
108
104
  - lib/ruby_llm/mcp/requests/completion_prompt.rb
109
105
  - lib/ruby_llm/mcp/requests/completion_resource.rb
@@ -149,7 +145,6 @@ metadata:
149
145
  homepage_uri: https://www.rubyllm-mcp.com
150
146
  source_code_uri: https://github.com/patvice/ruby_llm-mcp
151
147
  changelog_uri: https://github.com/patvice/ruby_llm-mcp/commits/main
152
- documentation_uri: https://www.rubyllm-mcp.com
153
148
  bug_tracker_uri: https://github.com/patvice/ruby_llm-mcp/issues
154
149
  rubygems_mfa_required: 'true'
155
150
  allowed_push_host: https://rubygems.org
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "ruby_llm/tool"
4
-
5
- module RubyLLM
6
- module MCP
7
- class Parameter < RubyLLM::Parameter
8
- attr_accessor :items, :properties, :enum, :union_type, :default, :title
9
-
10
- class << self
11
- def all_mcp_parameters?(parameters)
12
- parameters.is_a?(Hash) &&
13
- parameters.any? &&
14
- parameters.values.all? { |p| p.is_a?(RubyLLM::MCP::Parameter) }
15
- end
16
- end
17
-
18
- def initialize(name, type: "string", title: nil, desc: nil, required: true, default: nil, union_type: nil) # rubocop:disable Metrics/ParameterLists
19
- super(name, type: type.to_sym, desc: desc, required: required)
20
- @title = title
21
- @properties = {}
22
- @union_type = union_type
23
- @default = default
24
- end
25
-
26
- def item_type
27
- @items&.dig("type")&.to_sym
28
- end
29
-
30
- def as_json(*_args)
31
- to_h
32
- end
33
-
34
- def to_h
35
- {
36
- name: @name,
37
- type: @type,
38
- description: @desc,
39
- required: @required,
40
- default: @default,
41
- union_type: @union_type,
42
- items: @items&.to_h,
43
- properties: @properties&.values,
44
- enum: @enum
45
- }
46
- end
47
- end
48
- end
49
- end
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Providers
6
- module Anthropic
7
- module ComplexParameterSupport
8
- module_function
9
-
10
- def clean_parameters(parameters)
11
- parameters.transform_values do |param|
12
- mcp_build_properties(param).compact
13
- end
14
- end
15
-
16
- def required_parameters(parameters)
17
- parameters.select { |_, param| param.required }.keys
18
- end
19
-
20
- def mcp_build_properties(param) # rubocop:disable Metrics/MethodLength
21
- case param.type
22
- when :array
23
- if param.item_type == :object
24
- {
25
- type: param.type,
26
- title: param.title,
27
- description: param.description,
28
- items: { type: param.item_type, properties: clean_parameters(param.properties) }
29
- }.compact
30
- else
31
- {
32
- type: param.type,
33
- title: param.title,
34
- description: param.description,
35
- default: param.default,
36
- items: { type: param.item_type, enum: param.enum }.compact
37
- }.compact
38
- end
39
- when :object
40
- {
41
- type: param.type,
42
- title: param.title,
43
- description: param.description,
44
- properties: clean_parameters(param.properties),
45
- required: required_parameters(param.properties)
46
- }.compact
47
- when :union
48
- {
49
- param.union_type => param.properties.map { |property| mcp_build_properties(property) }
50
- }
51
- else
52
- {
53
- type: param.type,
54
- title: param.title,
55
- description: param.description
56
- }.compact
57
- end
58
- end
59
- end
60
- end
61
- end
62
- end
63
- end
64
-
65
- module RubyLLM::Providers::Anthropic::Tools
66
- alias original_clean_parameters clean_parameters
67
- alias original_required_parameters required_parameters
68
- module_function :original_clean_parameters, :original_required_parameters
69
-
70
- def clean_parameters(parameters)
71
- if RubyLLM::MCP::Parameter.all_mcp_parameters?(parameters)
72
- return RubyLLM::MCP::Providers::Anthropic::ComplexParameterSupport.clean_parameters(parameters)
73
- end
74
-
75
- original_clean_parameters(parameters)
76
- end
77
- module_function :clean_parameters
78
-
79
- def required_parameters(parameters)
80
- if RubyLLM::MCP::Parameter.all_mcp_parameters?(parameters)
81
- return RubyLLM::MCP::Providers::Anthropic::ComplexParameterSupport.required_parameters(parameters)
82
- end
83
-
84
- original_required_parameters(parameters)
85
- end
86
- module_function :required_parameters
87
- end
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Providers
6
- module Gemini
7
- module ComplexParameterSupport
8
- module_function
9
-
10
- # Format tool parameters for Gemini API
11
- def format_parameters(parameters)
12
- {
13
- type: "OBJECT",
14
- properties: parameters.transform_values { |param| mcp_build_properties(param) },
15
- required: parameters.select { |_, p| p.required }.keys.map(&:to_s)
16
- }
17
- end
18
-
19
- def mcp_build_properties(param) # rubocop:disable Metrics/MethodLength
20
- properties = case param.type
21
- when :array
22
- if param.item_type == :object
23
- {
24
- type: param_type_for_gemini(param.type),
25
- title: param.title,
26
- description: param.description,
27
- items: {
28
- type: param_type_for_gemini(param.item_type),
29
- properties: param.properties.transform_values { |value| mcp_build_properties(value) }
30
- }
31
- }.compact
32
- else
33
- {
34
- type: param_type_for_gemini(param.type),
35
- title: param.title,
36
- description: param.description,
37
- default: param.default,
38
- items: { type: param_type_for_gemini(param.item_type), enum: param.enum }.compact
39
- }.compact
40
- end
41
- when :object
42
- {
43
- type: param_type_for_gemini(param.type),
44
- title: param.title,
45
- description: param.description,
46
- properties: param.properties.transform_values { |value| mcp_build_properties(value) },
47
- required: param.properties.select { |_, p| p.required }.keys
48
- }.compact
49
- when :union
50
- {
51
- param.union_type => param.properties.map { |properties| mcp_build_properties(properties) }
52
- }
53
- else
54
- {
55
- type: param_type_for_gemini(param.type),
56
- title: param.title,
57
- description: param.description
58
- }
59
- end
60
-
61
- properties.compact
62
- end
63
-
64
- def param_type_for_gemini(type)
65
- RubyLLM::Providers::Gemini::Tools.param_type_for_gemini(type)
66
- end
67
- end
68
- end
69
- end
70
- end
71
- end
72
-
73
- module RubyLLM::Providers::Gemini::Tools
74
- alias original_format_parameters format_parameters
75
- module_function :original_format_parameters
76
- module_function :param_type_for_gemini
77
-
78
- def format_parameters(parameters)
79
- if RubyLLM::MCP::Parameter.all_mcp_parameters?(parameters)
80
- return RubyLLM::MCP::Providers::Gemini::ComplexParameterSupport.format_parameters(parameters)
81
- end
82
-
83
- original_format_parameters(parameters)
84
- end
85
- module_function :format_parameters
86
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Providers
6
- module OpenAI
7
- module ComplexParameterSupport
8
- module_function
9
-
10
- def param_schema(param) # rubocop:disable Metrics/MethodLength
11
- properties = case param.type
12
- when :array
13
- if param.item_type == :object
14
- {
15
- type: param.type,
16
- title: param.title,
17
- description: param.description,
18
- items: {
19
- type: param.item_type,
20
- properties: param.properties.transform_values { |value| param_schema(value) }
21
- }
22
- }.compact
23
- else
24
- {
25
- type: param.type,
26
- title: param.title,
27
- description: param.description,
28
- default: param.default,
29
- items: { type: param.item_type, enum: param.enum }.compact
30
- }.compact
31
- end
32
- when :object
33
- {
34
- type: param.type,
35
- title: param.title,
36
- description: param.description,
37
- properties: param.properties.transform_values { |value| param_schema(value) },
38
- required: param.properties.select { |_, p| p.required }.keys
39
- }.compact
40
- when :union
41
- {
42
- param.union_type => param.properties.map { |property| param_schema(property) }
43
- }
44
- else
45
- {
46
- type: param.type,
47
- title: param.title,
48
- description: param.description
49
- }.compact
50
- end
51
-
52
- properties.compact
53
- end
54
- end
55
- end
56
- end
57
- end
58
- end
59
-
60
- module RubyLLM::Providers::OpenAI::Tools
61
- alias original_param_schema param_schema
62
- module_function :original_param_schema
63
-
64
- def param_schema(param)
65
- if param.is_a?(RubyLLM::MCP::Parameter)
66
- return RubyLLM::MCP::Providers::OpenAI::ComplexParameterSupport.param_schema(param)
67
- end
68
-
69
- original_param_schema(param)
70
- end
71
- module_function :param_schema
72
- end