ruby_llm-mcp 0.7.1 → 0.8.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 +4 -4
- data/lib/generators/ruby_llm/mcp/{install_generator.rb → install/install_generator.rb} +4 -2
- data/lib/generators/ruby_llm/mcp/oauth/install_generator.rb +354 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/concerns/mcp_token_storage.rb.tt +114 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/concerns/user_mcp_oauth_concern.rb.tt +90 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/controllers/mcp_connections_controller.rb.tt +239 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/jobs/cleanup_expired_oauth_states_job.rb.tt +27 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/jobs/example_job.rb.tt +78 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/lib/mcp_client.rb.tt +68 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/migrations/create_mcp_oauth_credentials.rb.tt +19 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/migrations/create_mcp_oauth_states.rb.tt +21 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/models/mcp_oauth_credential.rb.tt +54 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/models/mcp_oauth_state.rb.tt +30 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/views/index.html.erb +646 -0
- data/lib/generators/ruby_llm/mcp/oauth/templates/views/show.html.erb +560 -0
- data/lib/ruby_llm/mcp/auth/browser/callback_handler.rb +71 -0
- data/lib/ruby_llm/mcp/auth/browser/callback_server.rb +30 -0
- data/lib/ruby_llm/mcp/auth/browser/http_server.rb +115 -0
- data/lib/ruby_llm/mcp/auth/browser/opener.rb +41 -0
- data/lib/ruby_llm/mcp/auth/browser/pages.rb +539 -0
- data/lib/ruby_llm/mcp/auth/browser_oauth_provider.rb +254 -0
- data/lib/ruby_llm/mcp/auth/client_registrar.rb +170 -0
- data/lib/ruby_llm/mcp/auth/discoverer.rb +124 -0
- data/lib/ruby_llm/mcp/auth/flows/authorization_code_flow.rb +105 -0
- data/lib/ruby_llm/mcp/auth/flows/client_credentials_flow.rb +66 -0
- data/lib/ruby_llm/mcp/auth/grant_strategies/authorization_code.rb +31 -0
- data/lib/ruby_llm/mcp/auth/grant_strategies/base.rb +31 -0
- data/lib/ruby_llm/mcp/auth/grant_strategies/client_credentials.rb +31 -0
- data/lib/ruby_llm/mcp/auth/http_response_handler.rb +65 -0
- data/lib/ruby_llm/mcp/auth/memory_storage.rb +72 -0
- data/lib/ruby_llm/mcp/auth/oauth_provider.rb +226 -0
- data/lib/ruby_llm/mcp/auth/security.rb +44 -0
- data/lib/ruby_llm/mcp/auth/session_manager.rb +56 -0
- data/lib/ruby_llm/mcp/auth/token_manager.rb +236 -0
- data/lib/ruby_llm/mcp/auth/url_builder.rb +78 -0
- data/lib/ruby_llm/mcp/auth.rb +359 -0
- data/lib/ruby_llm/mcp/client.rb +49 -0
- data/lib/ruby_llm/mcp/configuration.rb +39 -13
- data/lib/ruby_llm/mcp/coordinator.rb +11 -0
- data/lib/ruby_llm/mcp/errors.rb +11 -0
- data/lib/ruby_llm/mcp/railtie.rb +2 -10
- data/lib/ruby_llm/mcp/tool.rb +1 -1
- data/lib/ruby_llm/mcp/transport.rb +94 -1
- data/lib/ruby_llm/mcp/transports/sse.rb +116 -22
- data/lib/ruby_llm/mcp/transports/stdio.rb +4 -3
- data/lib/ruby_llm/mcp/transports/streamable_http.rb +81 -79
- data/lib/ruby_llm/mcp/version.rb +1 -1
- data/lib/ruby_llm/mcp.rb +10 -4
- metadata +40 -5
- /data/lib/generators/ruby_llm/mcp/{templates → install/templates}/initializer.rb +0 -0
- /data/lib/generators/ruby_llm/mcp/{templates → install/templates}/mcps.yml +0 -0
|
@@ -27,21 +27,6 @@ module RubyLLM
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
class OAuthOptions
|
|
31
|
-
attr_reader :issuer, :client_id, :client_secret, :scope
|
|
32
|
-
|
|
33
|
-
def initialize(issuer:, client_id:, client_secret:, scopes:)
|
|
34
|
-
@issuer = issuer
|
|
35
|
-
@client_id = client_id
|
|
36
|
-
@client_secret = client_secret
|
|
37
|
-
@scope = scopes
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def enabled?
|
|
41
|
-
@issuer && @client_id && @client_secret && @scope
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
30
|
# Options for starting SSE connections
|
|
46
31
|
class StartSSEOptions
|
|
47
32
|
attr_reader :resumption_token, :on_resumption_token, :replay_message_id
|
|
@@ -57,52 +42,52 @@ module RubyLLM
|
|
|
57
42
|
class StreamableHTTP
|
|
58
43
|
include Support::Timeout
|
|
59
44
|
|
|
60
|
-
attr_reader :session_id, :protocol_version, :coordinator
|
|
61
|
-
|
|
62
|
-
def initialize(
|
|
63
|
-
url:,
|
|
64
|
-
request_timeout:,
|
|
65
|
-
coordinator:,
|
|
66
|
-
headers: {},
|
|
67
|
-
reconnection: {},
|
|
68
|
-
version: :http2,
|
|
69
|
-
oauth: nil,
|
|
70
|
-
rate_limit: nil,
|
|
71
|
-
reconnection_options: nil,
|
|
72
|
-
session_id: nil
|
|
73
|
-
)
|
|
45
|
+
attr_reader :session_id, :protocol_version, :coordinator, :oauth_provider
|
|
46
|
+
|
|
47
|
+
def initialize(url:, request_timeout:, coordinator:, options: {})
|
|
74
48
|
@url = URI(url)
|
|
75
49
|
@coordinator = coordinator
|
|
76
50
|
@request_timeout = request_timeout
|
|
77
|
-
@headers = headers || {}
|
|
78
|
-
@session_id = session_id
|
|
79
51
|
|
|
80
|
-
|
|
81
|
-
|
|
52
|
+
extract_options(options)
|
|
53
|
+
initialize_state_variables
|
|
54
|
+
initialize_mutexes
|
|
55
|
+
|
|
56
|
+
@connection = create_connection
|
|
57
|
+
|
|
58
|
+
RubyLLM::MCP.logger.debug "OAuth provider: #{@oauth_provider ? 'present' : 'none'}" if @oauth_provider
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def extract_options(options)
|
|
62
|
+
@headers = options[:headers] || options["headers"] || {}
|
|
63
|
+
@session_id = options[:session_id] || options["session_id"]
|
|
64
|
+
@oauth_provider = options[:oauth_provider] || options["oauth_provider"]
|
|
65
|
+
@version = options[:version] || options["version"] || :http2
|
|
82
66
|
@protocol_version = nil
|
|
83
|
-
@session_id = session_id
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
@
|
|
68
|
+
reconnection = options[:reconnection] || options["reconnection"] || {}
|
|
69
|
+
@reconnection_options = options[:reconnection_options] || ReconnectionOptions.new(**reconnection)
|
|
87
70
|
|
|
88
|
-
|
|
89
|
-
@oauth_options = OAuthOptions.new(**oauth) unless oauth.nil?
|
|
71
|
+
rate_limit = options[:rate_limit] || options["rate_limit"]
|
|
90
72
|
@rate_limiter = Support::RateLimiter.new(**rate_limit) if rate_limit
|
|
73
|
+
end
|
|
91
74
|
|
|
75
|
+
def initialize_state_variables
|
|
76
|
+
@resource_metadata_url = nil
|
|
77
|
+
@client_id = SecureRandom.uuid
|
|
92
78
|
@id_counter = 0
|
|
93
|
-
@id_mutex = Mutex.new
|
|
94
79
|
@pending_requests = {}
|
|
95
|
-
@pending_mutex = Mutex.new
|
|
96
80
|
@running = true
|
|
97
81
|
@abort_controller = nil
|
|
98
82
|
@sse_thread = nil
|
|
99
|
-
@sse_mutex = Mutex.new
|
|
100
|
-
|
|
101
|
-
# Thread-safe collection of all HTTPX clients
|
|
102
83
|
@clients = []
|
|
103
|
-
|
|
84
|
+
end
|
|
104
85
|
|
|
105
|
-
|
|
86
|
+
def initialize_mutexes
|
|
87
|
+
@id_mutex = Mutex.new
|
|
88
|
+
@pending_mutex = Mutex.new
|
|
89
|
+
@sse_mutex = Mutex.new
|
|
90
|
+
@clients_mutex = Mutex.new
|
|
106
91
|
end
|
|
107
92
|
|
|
108
93
|
def request(body, add_id: true, wait_for_response: true)
|
|
@@ -242,17 +227,6 @@ module RubyLLM
|
|
|
242
227
|
}
|
|
243
228
|
)
|
|
244
229
|
|
|
245
|
-
if @oauth_options&.enabled?
|
|
246
|
-
client = client.plugin(:oauth).oauth_auth(
|
|
247
|
-
issuer: @oauth_options.issuer,
|
|
248
|
-
client_id: @oauth_options.client_id,
|
|
249
|
-
client_secret: @oauth_options.client_secret,
|
|
250
|
-
scope: @oauth_options.scope
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
client.with_access_token
|
|
254
|
-
end
|
|
255
|
-
|
|
256
230
|
register_client(client)
|
|
257
231
|
end
|
|
258
232
|
|
|
@@ -262,7 +236,25 @@ module RubyLLM
|
|
|
262
236
|
headers["mcp-session-id"] = @session_id if @session_id
|
|
263
237
|
headers["mcp-protocol-version"] = @protocol_version if @protocol_version
|
|
264
238
|
headers["X-CLIENT-ID"] = @client_id
|
|
265
|
-
headers["Origin"] = @
|
|
239
|
+
headers["Origin"] = @url.to_s
|
|
240
|
+
|
|
241
|
+
# Apply OAuth authorization if available
|
|
242
|
+
if @oauth_provider
|
|
243
|
+
RubyLLM::MCP.logger.debug "OAuth provider present, attempting to get token..."
|
|
244
|
+
RubyLLM::MCP.logger.debug " Server URL: #{@oauth_provider.server_url}"
|
|
245
|
+
|
|
246
|
+
token = @oauth_provider.access_token
|
|
247
|
+
if token
|
|
248
|
+
headers["Authorization"] = token.to_header
|
|
249
|
+
RubyLLM::MCP.logger.debug "✓ Applied OAuth authorization header: #{token.to_header[0..30]}..."
|
|
250
|
+
else
|
|
251
|
+
RubyLLM::MCP.logger.warn "✗ OAuth provider present but no valid token available!"
|
|
252
|
+
RubyLLM::MCP.logger.warn " This means the token is not in storage or has expired"
|
|
253
|
+
RubyLLM::MCP.logger.warn " Check that authentication completed successfully"
|
|
254
|
+
end
|
|
255
|
+
else
|
|
256
|
+
RubyLLM::MCP.logger.debug "No OAuth provider configured for this transport"
|
|
257
|
+
end
|
|
266
258
|
|
|
267
259
|
headers
|
|
268
260
|
end
|
|
@@ -320,16 +312,6 @@ module RubyLLM
|
|
|
320
312
|
}
|
|
321
313
|
)
|
|
322
314
|
|
|
323
|
-
if @oauth_options&.enabled?
|
|
324
|
-
client = client.plugin(:oauth).oauth_auth(
|
|
325
|
-
issuer: @oauth_options.issuer,
|
|
326
|
-
client_id: @oauth_options.client_id,
|
|
327
|
-
client_secret: @oauth_options.client_secret,
|
|
328
|
-
scope: @oauth_options.scope
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
client.with_access_token
|
|
332
|
-
end
|
|
333
315
|
register_client(client)
|
|
334
316
|
end
|
|
335
317
|
|
|
@@ -348,8 +330,12 @@ module RubyLLM
|
|
|
348
330
|
handle_accepted_response(original_message)
|
|
349
331
|
when 404
|
|
350
332
|
handle_session_expired
|
|
351
|
-
when
|
|
352
|
-
|
|
333
|
+
when 401
|
|
334
|
+
raise Errors::AuthenticationRequiredError.new(
|
|
335
|
+
message: "OAuth authentication required. Server returned 401 Unauthorized.",
|
|
336
|
+
code: 401
|
|
337
|
+
)
|
|
338
|
+
when 405
|
|
353
339
|
# Method not allowed - acceptable for some endpoints
|
|
354
340
|
nil
|
|
355
341
|
when 400...500
|
|
@@ -405,23 +391,38 @@ module RubyLLM
|
|
|
405
391
|
end
|
|
406
392
|
|
|
407
393
|
def handle_client_error(response)
|
|
394
|
+
response_body = response.respond_to?(:body) ? response.body.to_s : "Unknown error"
|
|
395
|
+
status_code = response.respond_to?(:status) ? response.status : "Unknown"
|
|
396
|
+
|
|
408
397
|
begin
|
|
409
|
-
# Safely access response body
|
|
410
|
-
response_body = response.respond_to?(:body) ? response.body.to_s : "Unknown error"
|
|
411
398
|
error_body = JSON.parse(response_body)
|
|
412
399
|
|
|
413
400
|
if error_body.is_a?(Hash) && error_body["error"]
|
|
414
|
-
error_message = error_body["error"]["message"] || error_body["error"]["code"]
|
|
401
|
+
error_message = error_body["error"]["message"] || error_body["error"]["code"] || error_body["error"].to_s
|
|
402
|
+
|
|
403
|
+
# If we still don't have a message, include the full error object
|
|
404
|
+
if error_message.to_s.strip.empty?
|
|
405
|
+
error_message = "Empty error (full response: #{response_body})"
|
|
406
|
+
end
|
|
415
407
|
|
|
416
408
|
if error_message.to_s.downcase.include?("session")
|
|
417
409
|
raise Errors::TransportError.new(
|
|
418
|
-
code:
|
|
410
|
+
code: status_code,
|
|
419
411
|
message: "Server error: #{error_message} (Current session ID: #{@session_id || 'none'})"
|
|
420
412
|
)
|
|
421
413
|
end
|
|
422
414
|
|
|
415
|
+
# Special handling for 403 Forbidden with OAuth
|
|
416
|
+
if status_code == 403 && @oauth_provider
|
|
417
|
+
raise Errors::TransportError.new(
|
|
418
|
+
code: status_code,
|
|
419
|
+
message: "Authorization failed (403 Forbidden): #{error_message}. \
|
|
420
|
+
Check token scope and resource permissions at #{@oauth_provider.server_url}."
|
|
421
|
+
)
|
|
422
|
+
end
|
|
423
|
+
|
|
423
424
|
raise Errors::TransportError.new(
|
|
424
|
-
code:
|
|
425
|
+
code: status_code,
|
|
425
426
|
message: "Server error: #{error_message}"
|
|
426
427
|
)
|
|
427
428
|
end
|
|
@@ -429,10 +430,6 @@ module RubyLLM
|
|
|
429
430
|
# Fall through to generic error
|
|
430
431
|
end
|
|
431
432
|
|
|
432
|
-
# Safely access response attributes
|
|
433
|
-
response_body = response.respond_to?(:body) ? response.body.to_s : "Unknown error"
|
|
434
|
-
status_code = response.respond_to?(:status) ? response.status : "Unknown"
|
|
435
|
-
|
|
436
433
|
raise Errors::TransportError.new(
|
|
437
434
|
code: status_code,
|
|
438
435
|
message: "HTTP client error: #{status_code} - #{response_body}"
|
|
@@ -492,7 +489,12 @@ module RubyLLM
|
|
|
492
489
|
# SSE stream established successfully
|
|
493
490
|
RubyLLM::MCP.logger.debug "SSE stream established"
|
|
494
491
|
# Response will be processed through callbacks
|
|
495
|
-
when
|
|
492
|
+
when 401
|
|
493
|
+
raise Errors::AuthenticationRequiredError.new(
|
|
494
|
+
message: "OAuth authentication required. Server returned 401 Unauthorized.",
|
|
495
|
+
code: 401
|
|
496
|
+
)
|
|
497
|
+
when 405
|
|
496
498
|
# Server doesn't support SSE - this is acceptable
|
|
497
499
|
RubyLLM::MCP.logger.info "Server does not support SSE streaming"
|
|
498
500
|
nil
|
data/lib/ruby_llm/mcp/version.rb
CHANGED
data/lib/ruby_llm/mcp.rb
CHANGED
|
@@ -61,10 +61,10 @@ module RubyLLM
|
|
|
61
61
|
tools.uniq(&:name)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
def mcp_configurations
|
|
65
|
+
config.mcp_configuration.each_with_object({}) do |config, acc|
|
|
66
|
+
acc[config[:name]] = config
|
|
67
|
+
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def configure
|
|
@@ -92,5 +92,11 @@ loader.inflector.inflect("sse" => "SSE")
|
|
|
92
92
|
loader.inflector.inflect("openai" => "OpenAI")
|
|
93
93
|
loader.inflector.inflect("streamable_http" => "StreamableHTTP")
|
|
94
94
|
loader.inflector.inflect("http_client" => "HTTPClient")
|
|
95
|
+
loader.inflector.inflect("oauth_provider" => "OAuthProvider")
|
|
96
|
+
loader.inflector.inflect("browser_oauth" => "BrowserOAuth")
|
|
97
|
+
loader.inflector.inflect("browser_oauth_provider" => "BrowserOAuthProvider")
|
|
98
|
+
loader.inflector.inflect("http_server" => "HttpServer")
|
|
99
|
+
loader.inflector.inflect("callback_handler" => "CallbackHandler")
|
|
100
|
+
loader.inflector.inflect("callback_server" => "CallbackServer")
|
|
95
101
|
|
|
96
102
|
loader.setup
|
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.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrick Vice
|
|
@@ -78,12 +78,46 @@ extra_rdoc_files: []
|
|
|
78
78
|
files:
|
|
79
79
|
- LICENSE
|
|
80
80
|
- README.md
|
|
81
|
-
- lib/generators/ruby_llm/mcp/install_generator.rb
|
|
82
|
-
- lib/generators/ruby_llm/mcp/templates/initializer.rb
|
|
83
|
-
- lib/generators/ruby_llm/mcp/templates/mcps.yml
|
|
81
|
+
- lib/generators/ruby_llm/mcp/install/install_generator.rb
|
|
82
|
+
- lib/generators/ruby_llm/mcp/install/templates/initializer.rb
|
|
83
|
+
- lib/generators/ruby_llm/mcp/install/templates/mcps.yml
|
|
84
|
+
- lib/generators/ruby_llm/mcp/oauth/install_generator.rb
|
|
85
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/concerns/mcp_token_storage.rb.tt
|
|
86
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/concerns/user_mcp_oauth_concern.rb.tt
|
|
87
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/controllers/mcp_connections_controller.rb.tt
|
|
88
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/jobs/cleanup_expired_oauth_states_job.rb.tt
|
|
89
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/jobs/example_job.rb.tt
|
|
90
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/lib/mcp_client.rb.tt
|
|
91
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/migrations/create_mcp_oauth_credentials.rb.tt
|
|
92
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/migrations/create_mcp_oauth_states.rb.tt
|
|
93
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/models/mcp_oauth_credential.rb.tt
|
|
94
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/models/mcp_oauth_state.rb.tt
|
|
95
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/views/index.html.erb
|
|
96
|
+
- lib/generators/ruby_llm/mcp/oauth/templates/views/show.html.erb
|
|
84
97
|
- lib/ruby_llm/chat.rb
|
|
85
98
|
- lib/ruby_llm/mcp.rb
|
|
86
99
|
- lib/ruby_llm/mcp/attachment.rb
|
|
100
|
+
- lib/ruby_llm/mcp/auth.rb
|
|
101
|
+
- lib/ruby_llm/mcp/auth/browser/callback_handler.rb
|
|
102
|
+
- lib/ruby_llm/mcp/auth/browser/callback_server.rb
|
|
103
|
+
- lib/ruby_llm/mcp/auth/browser/http_server.rb
|
|
104
|
+
- lib/ruby_llm/mcp/auth/browser/opener.rb
|
|
105
|
+
- lib/ruby_llm/mcp/auth/browser/pages.rb
|
|
106
|
+
- lib/ruby_llm/mcp/auth/browser_oauth_provider.rb
|
|
107
|
+
- lib/ruby_llm/mcp/auth/client_registrar.rb
|
|
108
|
+
- lib/ruby_llm/mcp/auth/discoverer.rb
|
|
109
|
+
- lib/ruby_llm/mcp/auth/flows/authorization_code_flow.rb
|
|
110
|
+
- lib/ruby_llm/mcp/auth/flows/client_credentials_flow.rb
|
|
111
|
+
- lib/ruby_llm/mcp/auth/grant_strategies/authorization_code.rb
|
|
112
|
+
- lib/ruby_llm/mcp/auth/grant_strategies/base.rb
|
|
113
|
+
- lib/ruby_llm/mcp/auth/grant_strategies/client_credentials.rb
|
|
114
|
+
- lib/ruby_llm/mcp/auth/http_response_handler.rb
|
|
115
|
+
- lib/ruby_llm/mcp/auth/memory_storage.rb
|
|
116
|
+
- lib/ruby_llm/mcp/auth/oauth_provider.rb
|
|
117
|
+
- lib/ruby_llm/mcp/auth/security.rb
|
|
118
|
+
- lib/ruby_llm/mcp/auth/session_manager.rb
|
|
119
|
+
- lib/ruby_llm/mcp/auth/token_manager.rb
|
|
120
|
+
- lib/ruby_llm/mcp/auth/url_builder.rb
|
|
87
121
|
- lib/ruby_llm/mcp/client.rb
|
|
88
122
|
- lib/ruby_llm/mcp/completion.rb
|
|
89
123
|
- lib/ruby_llm/mcp/configuration.rb
|
|
@@ -145,6 +179,7 @@ metadata:
|
|
|
145
179
|
homepage_uri: https://www.rubyllm-mcp.com
|
|
146
180
|
source_code_uri: https://github.com/patvice/ruby_llm-mcp
|
|
147
181
|
changelog_uri: https://github.com/patvice/ruby_llm-mcp/commits/main
|
|
182
|
+
documentation_uri: https://www.rubyllm-mcp.com/guides/
|
|
148
183
|
bug_tracker_uri: https://github.com/patvice/ruby_llm-mcp/issues
|
|
149
184
|
rubygems_mfa_required: 'true'
|
|
150
185
|
allowed_push_host: https://rubygems.org
|
|
@@ -162,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
162
197
|
- !ruby/object:Gem::Version
|
|
163
198
|
version: '0'
|
|
164
199
|
requirements: []
|
|
165
|
-
rubygems_version: 3.6.
|
|
200
|
+
rubygems_version: 3.6.9
|
|
166
201
|
specification_version: 4
|
|
167
202
|
summary: A RubyLLM MCP Client
|
|
168
203
|
test_files: []
|
|
File without changes
|
|
File without changes
|