ruby_llm-mcp 1.0.0 → 1.0.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 +4 -4
- data/lib/ruby_llm/mcp/auth/client_registrar.rb +2 -1
- data/lib/ruby_llm/mcp/auth/token_manager.rb +49 -33
- data/lib/ruby_llm/mcp/handlers/async_response.rb +18 -6
- data/lib/ruby_llm/mcp/handlers/promise.rb +19 -9
- data/lib/ruby_llm/mcp/native/cancellable_operation.rb +18 -11
- data/lib/ruby_llm/mcp/native/transports/streamable_http.rb +26 -1
- data/lib/ruby_llm/mcp/native/transports/support/timeout.rb +1 -0
- data/lib/ruby_llm/mcp/tool.rb +2 -2
- data/lib/ruby_llm/mcp/version.rb +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d19acd8152ac9ce9f7991474d6ff590175512f2e5f6524acf11e1e011e4a312c
|
|
4
|
+
data.tar.gz: 4ad593f7c6d7c5d6c5f225c7c6466226e0ff71f412c53e4542af2a65b9b59299
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b012fa00eaf0b132de3f716cd0e20045ef6188327dbb35770794bd0d6955d5f3396854718d8ae12ca5a09752aa5ce26bb7b347f4241ca7a4d243ca8c4ac6097
|
|
7
|
+
data.tar.gz: 7c0684175c0032fdac8a5ec1a5e1acb09d16fb5bb4fc81b64b12984a30e2e74ff96e6929dcc8d16780820febe871d151d1da41e00d7abf526c29f7110cb80143
|
|
@@ -123,7 +123,8 @@ module RubyLLM
|
|
|
123
123
|
def parse_registered_metadata(data, redirect_uri)
|
|
124
124
|
ClientMetadata.new(
|
|
125
125
|
redirect_uris: data["redirect_uris"] || [redirect_uri],
|
|
126
|
-
token_endpoint_auth_method: data["token_endpoint_auth_method"] ||
|
|
126
|
+
token_endpoint_auth_method: data["token_endpoint_auth_method"] ||
|
|
127
|
+
(data["client_secret"] ? "client_secret_post" : "none"),
|
|
127
128
|
grant_types: data["grant_types"] || %w[authorization_code refresh_token],
|
|
128
129
|
response_types: data["response_types"] || ["code"],
|
|
129
130
|
scope: data["scope"],
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "base64"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
3
6
|
module RubyLLM
|
|
4
7
|
module MCP
|
|
5
8
|
module Auth
|
|
@@ -26,8 +29,8 @@ module RubyLLM
|
|
|
26
29
|
registered_redirect_uri = client_info.metadata.redirect_uris.first
|
|
27
30
|
params = build_auth_code_params(client_info, code, pkce, registered_redirect_uri, server_url)
|
|
28
31
|
|
|
29
|
-
response = post_token_exchange(server_metadata, params)
|
|
30
|
-
response = retry_if_redirect_mismatch(response, server_metadata, params, registered_redirect_uri)
|
|
32
|
+
response = post_token_exchange(server_metadata, params, client_info)
|
|
33
|
+
response = retry_if_redirect_mismatch(response, server_metadata, params, registered_redirect_uri, client_info)
|
|
31
34
|
|
|
32
35
|
validate_token_response!(response, "Token exchange")
|
|
33
36
|
parse_token_response(response)
|
|
@@ -45,12 +48,11 @@ module RubyLLM
|
|
|
45
48
|
params = {
|
|
46
49
|
grant_type: "client_credentials",
|
|
47
50
|
client_id: client_info.client_id,
|
|
48
|
-
client_secret: client_info.client_secret,
|
|
49
51
|
scope: scope,
|
|
50
52
|
resource: server_url
|
|
51
53
|
}.compact
|
|
52
54
|
|
|
53
|
-
response = post_token_exchange(server_metadata, params)
|
|
55
|
+
response = post_token_exchange(server_metadata, params, client_info)
|
|
54
56
|
validate_token_response!(response, "Token exchange")
|
|
55
57
|
parse_token_response(response)
|
|
56
58
|
end
|
|
@@ -67,7 +69,7 @@ module RubyLLM
|
|
|
67
69
|
logger.debug("Refreshing access token")
|
|
68
70
|
|
|
69
71
|
params = build_refresh_params(client_info, token, server_url)
|
|
70
|
-
response = post_token_refresh(server_metadata, params)
|
|
72
|
+
response = post_token_refresh(server_metadata, params, client_info)
|
|
71
73
|
|
|
72
74
|
# Return nil on error responses
|
|
73
75
|
return nil if response.is_a?(HTTPX::ErrorResponse)
|
|
@@ -100,7 +102,7 @@ module RubyLLM
|
|
|
100
102
|
# @param server_url [String] MCP server URL
|
|
101
103
|
# @return [Hash] token exchange parameters
|
|
102
104
|
def build_auth_code_params(client_info, code, pkce, redirect_uri, server_url)
|
|
103
|
-
|
|
105
|
+
{
|
|
104
106
|
grant_type: "authorization_code",
|
|
105
107
|
code: code,
|
|
106
108
|
redirect_uri: redirect_uri,
|
|
@@ -108,9 +110,6 @@ module RubyLLM
|
|
|
108
110
|
code_verifier: pkce.code_verifier,
|
|
109
111
|
resource: server_url
|
|
110
112
|
}
|
|
111
|
-
|
|
112
|
-
add_client_secret_if_needed(params, client_info)
|
|
113
|
-
params
|
|
114
113
|
end
|
|
115
114
|
|
|
116
115
|
# Build parameters for token refresh
|
|
@@ -119,49 +118,65 @@ module RubyLLM
|
|
|
119
118
|
# @param server_url [String] MCP server URL
|
|
120
119
|
# @return [Hash] refresh parameters
|
|
121
120
|
def build_refresh_params(client_info, token, server_url)
|
|
122
|
-
|
|
121
|
+
{
|
|
123
122
|
grant_type: "refresh_token",
|
|
124
123
|
refresh_token: token.refresh_token,
|
|
125
124
|
client_id: client_info.client_id,
|
|
126
125
|
resource: server_url
|
|
127
126
|
}
|
|
128
|
-
|
|
129
|
-
add_client_secret_if_needed(params, client_info)
|
|
130
|
-
params
|
|
131
127
|
end
|
|
132
128
|
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
|
|
129
|
+
# Apply client authentication per RFC 6749 §2.3
|
|
130
|
+
# Supports "client_secret_post" (secret in body), "client_secret_basic" (HTTP Basic),
|
|
131
|
+
# and "none" (public client, no secret sent).
|
|
132
|
+
# @param params [Hash] token request form parameters (mutated in place)
|
|
133
|
+
# @param headers [Hash] HTTP headers (mutated in place)
|
|
134
|
+
# @param client_info [ClientInfo] client info with secret and auth method
|
|
135
|
+
def apply_client_auth!(params, headers, client_info)
|
|
137
136
|
return unless client_info.client_secret
|
|
138
|
-
return unless client_info.metadata.token_endpoint_auth_method == "client_secret_post"
|
|
139
137
|
|
|
140
|
-
|
|
138
|
+
case client_auth_method(client_info)
|
|
139
|
+
when "client_secret_post"
|
|
140
|
+
params[:client_secret] = client_info.client_secret
|
|
141
|
+
when "client_secret_basic"
|
|
142
|
+
client_id = URI.encode_www_form_component(client_info.client_id)
|
|
143
|
+
client_secret = URI.encode_www_form_component(client_info.client_secret)
|
|
144
|
+
credentials = Base64.strict_encode64("#{client_id}:#{client_secret}")
|
|
145
|
+
headers["Authorization"] = "Basic #{credentials}"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Resolve the client authentication method, including registrations cached by older versions.
|
|
150
|
+
# Older versions stored an omitted method as "none", even when the server returned a secret.
|
|
151
|
+
# @param client_info [ClientInfo] client info with secret and auth method
|
|
152
|
+
# @return [String, nil] effective token endpoint authentication method
|
|
153
|
+
def client_auth_method(client_info)
|
|
154
|
+
auth_method = client_info.metadata&.token_endpoint_auth_method
|
|
155
|
+
return "client_secret_post" if client_info.client_secret && [nil, "none"].include?(auth_method)
|
|
156
|
+
|
|
157
|
+
auth_method
|
|
141
158
|
end
|
|
142
159
|
|
|
143
160
|
# Post token exchange request
|
|
144
161
|
# @param server_metadata [ServerMetadata] server metadata
|
|
145
162
|
# @param params [Hash] form parameters
|
|
163
|
+
# @param client_info [ClientInfo, nil] client info for authentication
|
|
146
164
|
# @return [HTTPX::Response] HTTP response
|
|
147
|
-
def post_token_exchange(server_metadata, params)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
form: params
|
|
152
|
-
)
|
|
165
|
+
def post_token_exchange(server_metadata, params, client_info = nil)
|
|
166
|
+
headers = { "Content-Type" => "application/x-www-form-urlencoded" }
|
|
167
|
+
apply_client_auth!(params, headers, client_info) if client_info
|
|
168
|
+
http_client.post(server_metadata.token_endpoint, headers:, form: params)
|
|
153
169
|
end
|
|
154
170
|
|
|
155
171
|
# Post token refresh request
|
|
156
172
|
# @param server_metadata [ServerMetadata] server metadata
|
|
157
173
|
# @param params [Hash] form parameters
|
|
174
|
+
# @param client_info [ClientInfo, nil] client info for authentication
|
|
158
175
|
# @return [HTTPX::Response] HTTP response
|
|
159
|
-
def post_token_refresh(server_metadata, params)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
form: params
|
|
164
|
-
)
|
|
176
|
+
def post_token_refresh(server_metadata, params, client_info = nil)
|
|
177
|
+
headers = { "Content-Type" => "application/x-www-form-urlencoded" }
|
|
178
|
+
apply_client_auth!(params, headers, client_info) if client_info
|
|
179
|
+
response = http_client.post(server_metadata.token_endpoint, headers:, form: params)
|
|
165
180
|
|
|
166
181
|
if response.is_a?(HTTPX::ErrorResponse)
|
|
167
182
|
logger.warn("Token refresh failed: #{response.error&.message || 'Request failed'}")
|
|
@@ -176,8 +191,9 @@ module RubyLLM
|
|
|
176
191
|
# @param server_metadata [ServerMetadata] server metadata
|
|
177
192
|
# @param params [Hash] exchange parameters
|
|
178
193
|
# @param registered_redirect_uri [String] registered redirect URI
|
|
194
|
+
# @param client_info [ClientInfo] client info for authentication
|
|
179
195
|
# @return [HTTPX::Response] response (possibly retried)
|
|
180
|
-
def retry_if_redirect_mismatch(response, server_metadata, params, registered_redirect_uri)
|
|
196
|
+
def retry_if_redirect_mismatch(response, server_metadata, params, registered_redirect_uri, client_info)
|
|
181
197
|
# Don't retry on error responses
|
|
182
198
|
return response if response.is_a?(HTTPX::ErrorResponse)
|
|
183
199
|
return response if response.status == 200
|
|
@@ -188,7 +204,7 @@ module RubyLLM
|
|
|
188
204
|
|
|
189
205
|
logger.warn("Redirect URI mismatch, retrying with: #{redirect_hint[:expected]}")
|
|
190
206
|
params[:redirect_uri] = redirect_hint[:expected]
|
|
191
|
-
post_token_exchange(server_metadata, params)
|
|
207
|
+
post_token_exchange(server_metadata, params, client_info)
|
|
192
208
|
end
|
|
193
209
|
|
|
194
210
|
# Validate token response
|
|
@@ -5,7 +5,7 @@ module RubyLLM
|
|
|
5
5
|
module Handlers
|
|
6
6
|
# Represents an async response for deferred completion
|
|
7
7
|
class AsyncResponse
|
|
8
|
-
attr_reader :elicitation_id
|
|
8
|
+
attr_reader :elicitation_id
|
|
9
9
|
|
|
10
10
|
VALID_STATES = %i[pending completed rejected cancelled timed_out].freeze
|
|
11
11
|
|
|
@@ -95,27 +95,27 @@ module RubyLLM
|
|
|
95
95
|
|
|
96
96
|
# Check if operation is pending
|
|
97
97
|
def pending?
|
|
98
|
-
@state == :pending
|
|
98
|
+
@mutex.synchronize { @state == :pending }
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
# Check if operation is completed
|
|
102
102
|
def completed?
|
|
103
|
-
@state == :completed
|
|
103
|
+
@mutex.synchronize { @state == :completed }
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
# Check if operation is rejected
|
|
107
107
|
def rejected?
|
|
108
|
-
@state == :rejected
|
|
108
|
+
@mutex.synchronize { @state == :rejected }
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
# Check if operation is cancelled
|
|
112
112
|
def cancelled?
|
|
113
|
-
@state == :cancelled
|
|
113
|
+
@mutex.synchronize { @state == :cancelled }
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
# Check if operation timed out
|
|
117
117
|
def timed_out?
|
|
118
|
-
@state == :timed_out
|
|
118
|
+
@mutex.synchronize { @state == :timed_out }
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
# Check if operation is finished (any terminal state)
|
|
@@ -123,6 +123,18 @@ module RubyLLM
|
|
|
123
123
|
!pending?
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
+
def state
|
|
127
|
+
@mutex.synchronize { @state }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def result
|
|
131
|
+
@mutex.synchronize { @result }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def error
|
|
135
|
+
@mutex.synchronize { @error }
|
|
136
|
+
end
|
|
137
|
+
|
|
126
138
|
private
|
|
127
139
|
|
|
128
140
|
# Transition to new state (thread-safe)
|
|
@@ -5,8 +5,6 @@ module RubyLLM
|
|
|
5
5
|
module Handlers
|
|
6
6
|
# Promise implementation for async operations
|
|
7
7
|
class Promise
|
|
8
|
-
attr_reader :state, :value, :reason
|
|
9
|
-
|
|
10
8
|
# Initialize a new promise
|
|
11
9
|
def initialize
|
|
12
10
|
@state = :pending
|
|
@@ -120,17 +118,17 @@ module RubyLLM
|
|
|
120
118
|
|
|
121
119
|
# Check if promise is pending
|
|
122
120
|
def pending?
|
|
123
|
-
@state == :pending
|
|
121
|
+
@mutex.synchronize { @state == :pending }
|
|
124
122
|
end
|
|
125
123
|
|
|
126
124
|
# Check if promise is fulfilled
|
|
127
125
|
def fulfilled?
|
|
128
|
-
@state == :fulfilled
|
|
126
|
+
@mutex.synchronize { @state == :fulfilled }
|
|
129
127
|
end
|
|
130
128
|
|
|
131
129
|
# Check if promise is rejected
|
|
132
130
|
def rejected?
|
|
133
|
-
@state == :rejected
|
|
131
|
+
@mutex.synchronize { @state == :rejected }
|
|
134
132
|
end
|
|
135
133
|
|
|
136
134
|
# Check if promise is settled (fulfilled or rejected)
|
|
@@ -146,7 +144,7 @@ module RubyLLM
|
|
|
146
144
|
# Wait until promise is settled
|
|
147
145
|
if timeout
|
|
148
146
|
deadline = Time.now + timeout
|
|
149
|
-
while pending
|
|
147
|
+
while @state == :pending
|
|
150
148
|
remaining = deadline - Time.now
|
|
151
149
|
if remaining <= 0
|
|
152
150
|
raise Timeout::Error, "Promise timed out after #{timeout} seconds"
|
|
@@ -155,15 +153,27 @@ module RubyLLM
|
|
|
155
153
|
@condition.wait(@mutex, remaining)
|
|
156
154
|
end
|
|
157
155
|
else
|
|
158
|
-
@condition.wait(@mutex) while pending
|
|
156
|
+
@condition.wait(@mutex) while @state == :pending
|
|
159
157
|
end
|
|
160
158
|
|
|
161
159
|
# Return value or raise error
|
|
162
|
-
return @value if fulfilled
|
|
163
|
-
raise @reason if rejected
|
|
160
|
+
return @value if @state == :fulfilled
|
|
161
|
+
raise @reason if @state == :rejected
|
|
164
162
|
end
|
|
165
163
|
end
|
|
166
164
|
|
|
165
|
+
def state
|
|
166
|
+
@mutex.synchronize { @state }
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def value
|
|
170
|
+
@mutex.synchronize { @value }
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def reason
|
|
174
|
+
@mutex.synchronize { @reason }
|
|
175
|
+
end
|
|
176
|
+
|
|
167
177
|
private
|
|
168
178
|
|
|
169
179
|
# Execute a callback safely
|
|
@@ -6,7 +6,7 @@ module RubyLLM
|
|
|
6
6
|
# Wraps server-initiated requests to support cancellation.
|
|
7
7
|
# The operation tracks terminal state so cancellation outcomes are explicit.
|
|
8
8
|
class CancellableOperation
|
|
9
|
-
attr_reader :request_id
|
|
9
|
+
attr_reader :request_id
|
|
10
10
|
|
|
11
11
|
def initialize(request_id)
|
|
12
12
|
@request_id = request_id
|
|
@@ -61,22 +61,21 @@ module RubyLLM
|
|
|
61
61
|
def execute(&)
|
|
62
62
|
return nil if cancelled?
|
|
63
63
|
|
|
64
|
-
@mutex.synchronize do
|
|
64
|
+
worker = @mutex.synchronize do
|
|
65
65
|
return nil if %i[cancelled cancelling].include?(@state)
|
|
66
66
|
|
|
67
67
|
@state = :running
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@error = e
|
|
68
|
+
@thread = Thread.new do
|
|
69
|
+
Thread.current.abort_on_exception = false
|
|
70
|
+
begin
|
|
71
|
+
@result = yield
|
|
72
|
+
rescue Errors::RequestCancelled, StandardError => e
|
|
73
|
+
@error = e
|
|
74
|
+
end
|
|
76
75
|
end
|
|
77
76
|
end
|
|
78
77
|
|
|
79
|
-
|
|
78
|
+
worker.join
|
|
80
79
|
raise @error if @error && !@error.is_a?(Errors::RequestCancelled)
|
|
81
80
|
|
|
82
81
|
@result
|
|
@@ -88,6 +87,14 @@ module RubyLLM
|
|
|
88
87
|
@thread = nil
|
|
89
88
|
end
|
|
90
89
|
end
|
|
90
|
+
|
|
91
|
+
def state
|
|
92
|
+
@mutex.synchronize { @state }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def thread
|
|
96
|
+
@mutex.synchronize { @thread }
|
|
97
|
+
end
|
|
91
98
|
end
|
|
92
99
|
end
|
|
93
100
|
end
|
|
@@ -406,6 +406,7 @@ module RubyLLM
|
|
|
406
406
|
handle_authorization_challenge(response, request_id, original_message)
|
|
407
407
|
when 405
|
|
408
408
|
# Method not allowed - acceptable for some endpoints
|
|
409
|
+
fail_pending_request(request_id, response) if request_id
|
|
409
410
|
nil
|
|
410
411
|
when 400...500
|
|
411
412
|
handle_client_error(response)
|
|
@@ -418,6 +419,19 @@ module RubyLLM
|
|
|
418
419
|
end
|
|
419
420
|
end
|
|
420
421
|
|
|
422
|
+
def fail_pending_request(request_id, response)
|
|
423
|
+
response_body = response.respond_to?(:body) ? response.body.to_s : "Unknown error"
|
|
424
|
+
error = Errors::TransportError.new(
|
|
425
|
+
code: response.status,
|
|
426
|
+
message: "HTTP request failed: #{response.status} - #{response_body}"
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
@pending_mutex.synchronize do
|
|
430
|
+
queue = @pending_requests.delete(request_id.to_s)
|
|
431
|
+
queue&.push(error)
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
421
435
|
def handle_success_response(response, request_id, _original_message)
|
|
422
436
|
content_type = response.respond_to?(:headers) ? response.headers["content-type"] : nil
|
|
423
437
|
|
|
@@ -479,7 +493,7 @@ module RubyLLM
|
|
|
479
493
|
def handle_oauth_authorization_error(response, status_code)
|
|
480
494
|
response_body = response.respond_to?(:body) ? response.body.to_s : ""
|
|
481
495
|
error_body = JSON.parse(response_body)
|
|
482
|
-
error_message = error_body
|
|
496
|
+
error_message = oauth_error_message(error_body) || "Authorization failed"
|
|
483
497
|
|
|
484
498
|
raise Errors::TransportError.new(
|
|
485
499
|
code: status_code,
|
|
@@ -492,6 +506,17 @@ module RubyLLM
|
|
|
492
506
|
)
|
|
493
507
|
end
|
|
494
508
|
|
|
509
|
+
# A 403 body can be either a JSON-RPC error ({ "error" => { "message" => ... } }) or an
|
|
510
|
+
# RFC 6749 OAuth error ({ "error" => "invalid_token", "error_description" => ... })
|
|
511
|
+
def oauth_error_message(error_body)
|
|
512
|
+
return unless error_body.is_a?(Hash)
|
|
513
|
+
|
|
514
|
+
case error_body["error"]
|
|
515
|
+
when Hash then error_body["error"]["message"]
|
|
516
|
+
when String then error_body["error_description"] || error_body["error"]
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
495
520
|
def handle_json_error_response(response, status_code)
|
|
496
521
|
response_body = response.respond_to?(:body) ? response.body.to_s : "Unknown error"
|
|
497
522
|
error_body = JSON.parse(response_body)
|
|
@@ -22,6 +22,7 @@ module RubyLLM
|
|
|
22
22
|
result
|
|
23
23
|
else
|
|
24
24
|
worker.kill # stop the thread (can still have some risk if shared resources)
|
|
25
|
+
worker.join(0.1)
|
|
25
26
|
raise RubyLLM::MCP::Errors::TimeoutError.new(
|
|
26
27
|
message: "Request timed out after #{seconds} seconds",
|
|
27
28
|
request_id: request_id
|
data/lib/ruby_llm/mcp/tool.rb
CHANGED
|
@@ -8,9 +8,9 @@ module RubyLLM
|
|
|
8
8
|
def initialize(annotation)
|
|
9
9
|
@title = annotation["title"] || ""
|
|
10
10
|
@read_only_hint = annotation["readOnlyHint"] || false
|
|
11
|
-
@destructive_hint = annotation
|
|
11
|
+
@destructive_hint = annotation.fetch("destructiveHint", true)
|
|
12
12
|
@idempotent_hint = annotation["idempotentHint"] || false
|
|
13
|
-
@open_world_hint = annotation
|
|
13
|
+
@open_world_hint = annotation.fetch("openWorldHint", true)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def to_h
|
data/lib/ruby_llm/mcp/version.rb
CHANGED
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: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrick Vice
|
|
@@ -27,16 +27,22 @@ dependencies:
|
|
|
27
27
|
name: json-schema
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '5.0'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '7'
|
|
33
36
|
type: :runtime
|
|
34
37
|
prerelease: false
|
|
35
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
39
|
requirements:
|
|
37
|
-
- - "
|
|
40
|
+
- - ">="
|
|
38
41
|
- !ruby/object:Gem::Version
|
|
39
42
|
version: '5.0'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '7'
|
|
40
46
|
- !ruby/object:Gem::Dependency
|
|
41
47
|
name: json_schemer
|
|
42
48
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -238,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
238
244
|
- !ruby/object:Gem::Version
|
|
239
245
|
version: '0'
|
|
240
246
|
requirements: []
|
|
241
|
-
rubygems_version: 4.0.
|
|
247
|
+
rubygems_version: 4.0.10
|
|
242
248
|
specification_version: 4
|
|
243
249
|
summary: A RubyLLM MCP Client
|
|
244
250
|
test_files: []
|