ruby_llm-mcp 0.7.1 → 1.0.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.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +144 -162
  3. data/lib/generators/ruby_llm/mcp/{install_generator.rb → install/install_generator.rb} +4 -2
  4. data/lib/generators/ruby_llm/mcp/{templates → install/templates}/initializer.rb +21 -4
  5. data/lib/generators/ruby_llm/mcp/install/templates/mcps.yml +29 -0
  6. data/lib/generators/ruby_llm/mcp/oauth/install_generator.rb +354 -0
  7. data/lib/generators/ruby_llm/mcp/oauth/templates/concerns/mcp_token_storage.rb.tt +114 -0
  8. data/lib/generators/ruby_llm/mcp/oauth/templates/concerns/user_mcp_oauth_concern.rb.tt +90 -0
  9. data/lib/generators/ruby_llm/mcp/oauth/templates/controllers/mcp_connections_controller.rb.tt +239 -0
  10. data/lib/generators/ruby_llm/mcp/oauth/templates/jobs/cleanup_expired_oauth_states_job.rb.tt +27 -0
  11. data/lib/generators/ruby_llm/mcp/oauth/templates/jobs/example_job.rb.tt +78 -0
  12. data/lib/generators/ruby_llm/mcp/oauth/templates/lib/mcp_client.rb.tt +68 -0
  13. data/lib/generators/ruby_llm/mcp/oauth/templates/migrations/create_mcp_oauth_credentials.rb.tt +19 -0
  14. data/lib/generators/ruby_llm/mcp/oauth/templates/migrations/create_mcp_oauth_states.rb.tt +21 -0
  15. data/lib/generators/ruby_llm/mcp/oauth/templates/models/mcp_oauth_credential.rb.tt +54 -0
  16. data/lib/generators/ruby_llm/mcp/oauth/templates/models/mcp_oauth_state.rb.tt +30 -0
  17. data/lib/generators/ruby_llm/mcp/oauth/templates/views/index.html.erb +646 -0
  18. data/lib/generators/ruby_llm/mcp/oauth/templates/views/show.html.erb +560 -0
  19. data/lib/ruby_llm/mcp/adapters/base_adapter.rb +215 -0
  20. data/lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb +413 -0
  21. data/lib/ruby_llm/mcp/adapters/mcp_transports/coordinator_stub.rb +41 -0
  22. data/lib/ruby_llm/mcp/adapters/mcp_transports/sse.rb +56 -0
  23. data/lib/ruby_llm/mcp/adapters/mcp_transports/stdio.rb +56 -0
  24. data/lib/ruby_llm/mcp/adapters/mcp_transports/streamable_http.rb +90 -0
  25. data/lib/ruby_llm/mcp/adapters/ruby_llm_adapter.rb +216 -0
  26. data/lib/ruby_llm/mcp/auth/browser/callback_handler.rb +71 -0
  27. data/lib/ruby_llm/mcp/auth/browser/callback_server.rb +36 -0
  28. data/lib/ruby_llm/mcp/auth/browser/http_server.rb +112 -0
  29. data/lib/ruby_llm/mcp/auth/browser/opener.rb +39 -0
  30. data/lib/ruby_llm/mcp/auth/browser/pages.rb +607 -0
  31. data/lib/ruby_llm/mcp/auth/browser_oauth_provider.rb +427 -0
  32. data/lib/ruby_llm/mcp/auth/client_registrar.rb +170 -0
  33. data/lib/ruby_llm/mcp/auth/discoverer.rb +255 -0
  34. data/lib/ruby_llm/mcp/auth/flows/authorization_code_flow.rb +122 -0
  35. data/lib/ruby_llm/mcp/auth/flows/client_credentials_flow.rb +67 -0
  36. data/lib/ruby_llm/mcp/auth/grant_strategies/authorization_code.rb +31 -0
  37. data/lib/ruby_llm/mcp/auth/grant_strategies/base.rb +31 -0
  38. data/lib/ruby_llm/mcp/auth/grant_strategies/client_credentials.rb +31 -0
  39. data/lib/ruby_llm/mcp/auth/http_response_handler.rb +63 -0
  40. data/lib/ruby_llm/mcp/auth/memory_storage.rb +91 -0
  41. data/lib/ruby_llm/mcp/auth/oauth_provider.rb +341 -0
  42. data/lib/ruby_llm/mcp/auth/security.rb +44 -0
  43. data/lib/ruby_llm/mcp/auth/session_manager.rb +54 -0
  44. data/lib/ruby_llm/mcp/auth/token_manager.rb +307 -0
  45. data/lib/ruby_llm/mcp/auth/transport_oauth_helper.rb +107 -0
  46. data/lib/ruby_llm/mcp/auth/url_builder.rb +135 -0
  47. data/lib/ruby_llm/mcp/auth.rb +371 -0
  48. data/lib/ruby_llm/mcp/client.rb +312 -35
  49. data/lib/ruby_llm/mcp/configuration.rb +199 -24
  50. data/lib/ruby_llm/mcp/elicitation.rb +261 -14
  51. data/lib/ruby_llm/mcp/errors.rb +29 -0
  52. data/lib/ruby_llm/mcp/extensions/apps/constants.rb +28 -0
  53. data/lib/ruby_llm/mcp/extensions/apps/resource_metadata.rb +24 -0
  54. data/lib/ruby_llm/mcp/extensions/apps/tool_metadata.rb +45 -0
  55. data/lib/ruby_llm/mcp/extensions/configuration.rb +72 -0
  56. data/lib/ruby_llm/mcp/extensions/constants.rb +16 -0
  57. data/lib/ruby_llm/mcp/extensions/registry.rb +85 -0
  58. data/lib/ruby_llm/mcp/handlers/approval_decision.rb +90 -0
  59. data/lib/ruby_llm/mcp/handlers/async_response.rb +181 -0
  60. data/lib/ruby_llm/mcp/handlers/concerns/approval_actions.rb +42 -0
  61. data/lib/ruby_llm/mcp/handlers/concerns/async_execution.rb +80 -0
  62. data/lib/ruby_llm/mcp/handlers/concerns/elicitation_actions.rb +42 -0
  63. data/lib/ruby_llm/mcp/handlers/concerns/error_handling.rb +29 -0
  64. data/lib/ruby_llm/mcp/handlers/concerns/guard_checks.rb +72 -0
  65. data/lib/ruby_llm/mcp/handlers/concerns/lifecycle.rb +84 -0
  66. data/lib/ruby_llm/mcp/handlers/concerns/logging.rb +19 -0
  67. data/lib/ruby_llm/mcp/handlers/concerns/model_filtering.rb +36 -0
  68. data/lib/ruby_llm/mcp/handlers/concerns/options.rb +83 -0
  69. data/lib/ruby_llm/mcp/handlers/concerns/registry_integration.rb +54 -0
  70. data/lib/ruby_llm/mcp/handlers/concerns/sampling_actions.rb +84 -0
  71. data/lib/ruby_llm/mcp/handlers/concerns/timeouts.rb +52 -0
  72. data/lib/ruby_llm/mcp/handlers/concerns/tool_filtering.rb +50 -0
  73. data/lib/ruby_llm/mcp/handlers/elicitation_handler.rb +58 -0
  74. data/lib/ruby_llm/mcp/handlers/elicitation_registry.rb +203 -0
  75. data/lib/ruby_llm/mcp/handlers/human_in_the_loop_handler.rb +93 -0
  76. data/lib/ruby_llm/mcp/handlers/human_in_the_loop_registry.rb +271 -0
  77. data/lib/ruby_llm/mcp/handlers/promise.rb +192 -0
  78. data/lib/ruby_llm/mcp/handlers/sampling_handler.rb +64 -0
  79. data/lib/ruby_llm/mcp/handlers.rb +14 -0
  80. data/lib/ruby_llm/mcp/native/cancellable_operation.rb +94 -0
  81. data/lib/ruby_llm/mcp/native/client.rb +551 -0
  82. data/lib/ruby_llm/mcp/native/json_rpc.rb +170 -0
  83. data/lib/ruby_llm/mcp/native/messages/helpers.rb +39 -0
  84. data/lib/ruby_llm/mcp/native/messages/notifications.rb +60 -0
  85. data/lib/ruby_llm/mcp/native/messages/requests.rb +267 -0
  86. data/lib/ruby_llm/mcp/native/messages/responses.rb +114 -0
  87. data/lib/ruby_llm/mcp/native/messages.rb +43 -0
  88. data/lib/ruby_llm/mcp/native/notification.rb +16 -0
  89. data/lib/ruby_llm/mcp/native/protocol.rb +79 -0
  90. data/lib/ruby_llm/mcp/native/response_handler.rb +220 -0
  91. data/lib/ruby_llm/mcp/native/task_registry.rb +62 -0
  92. data/lib/ruby_llm/mcp/native/transport.rb +88 -0
  93. data/lib/ruby_llm/mcp/native/transports/sse.rb +655 -0
  94. data/lib/ruby_llm/mcp/native/transports/stdio.rb +367 -0
  95. data/lib/ruby_llm/mcp/native/transports/streamable_http.rb +1024 -0
  96. data/lib/ruby_llm/mcp/native/transports/support/http_client.rb +28 -0
  97. data/lib/ruby_llm/mcp/native/transports/support/rate_limiter.rb +49 -0
  98. data/lib/ruby_llm/mcp/native/transports/support/timeout.rb +36 -0
  99. data/lib/ruby_llm/mcp/native.rb +12 -0
  100. data/lib/ruby_llm/mcp/notification_handler.rb +43 -5
  101. data/lib/ruby_llm/mcp/prompt.rb +7 -7
  102. data/lib/ruby_llm/mcp/railtie.rb +7 -13
  103. data/lib/ruby_llm/mcp/resource.rb +17 -8
  104. data/lib/ruby_llm/mcp/resource_template.rb +8 -7
  105. data/lib/ruby_llm/mcp/result.rb +8 -4
  106. data/lib/ruby_llm/mcp/roots.rb +4 -4
  107. data/lib/ruby_llm/mcp/sample.rb +83 -13
  108. data/lib/ruby_llm/mcp/schema_validator.rb +33 -0
  109. data/lib/ruby_llm/mcp/server_capabilities.rb +41 -0
  110. data/lib/ruby_llm/mcp/task.rb +65 -0
  111. data/lib/ruby_llm/mcp/tool.rb +33 -27
  112. data/lib/ruby_llm/mcp/version.rb +1 -1
  113. data/lib/ruby_llm/mcp.rb +37 -7
  114. data/lib/tasks/smoke.rake +66 -0
  115. metadata +115 -39
  116. data/lib/generators/ruby_llm/mcp/templates/mcps.yml +0 -9
  117. data/lib/ruby_llm/mcp/coordinator.rb +0 -293
  118. data/lib/ruby_llm/mcp/notifications/cancelled.rb +0 -32
  119. data/lib/ruby_llm/mcp/notifications/initialize.rb +0 -24
  120. data/lib/ruby_llm/mcp/notifications/roots_list_change.rb +0 -26
  121. data/lib/ruby_llm/mcp/protocol.rb +0 -34
  122. data/lib/ruby_llm/mcp/requests/completion_prompt.rb +0 -50
  123. data/lib/ruby_llm/mcp/requests/completion_resource.rb +0 -50
  124. data/lib/ruby_llm/mcp/requests/initialization.rb +0 -34
  125. data/lib/ruby_llm/mcp/requests/logging_set_level.rb +0 -28
  126. data/lib/ruby_llm/mcp/requests/ping.rb +0 -24
  127. data/lib/ruby_llm/mcp/requests/prompt_call.rb +0 -32
  128. data/lib/ruby_llm/mcp/requests/prompt_list.rb +0 -31
  129. data/lib/ruby_llm/mcp/requests/resource_list.rb +0 -31
  130. data/lib/ruby_llm/mcp/requests/resource_read.rb +0 -30
  131. data/lib/ruby_llm/mcp/requests/resource_template_list.rb +0 -31
  132. data/lib/ruby_llm/mcp/requests/resources_subscribe.rb +0 -30
  133. data/lib/ruby_llm/mcp/requests/shared/meta.rb +0 -32
  134. data/lib/ruby_llm/mcp/requests/shared/pagination.rb +0 -17
  135. data/lib/ruby_llm/mcp/requests/tool_call.rb +0 -35
  136. data/lib/ruby_llm/mcp/requests/tool_list.rb +0 -31
  137. data/lib/ruby_llm/mcp/response_handler.rb +0 -67
  138. data/lib/ruby_llm/mcp/responses/elicitation.rb +0 -33
  139. data/lib/ruby_llm/mcp/responses/error.rb +0 -33
  140. data/lib/ruby_llm/mcp/responses/ping.rb +0 -28
  141. data/lib/ruby_llm/mcp/responses/roots_list.rb +0 -31
  142. data/lib/ruby_llm/mcp/responses/sampling_create_message.rb +0 -50
  143. data/lib/ruby_llm/mcp/transport.rb +0 -58
  144. data/lib/ruby_llm/mcp/transports/sse.rb +0 -341
  145. data/lib/ruby_llm/mcp/transports/stdio.rb +0 -230
  146. data/lib/ruby_llm/mcp/transports/streamable_http.rb +0 -723
  147. data/lib/ruby_llm/mcp/transports/support/http_client.rb +0 -28
  148. data/lib/ruby_llm/mcp/transports/support/rate_limit.rb +0 -47
  149. data/lib/ruby_llm/mcp/transports/support/timeout.rb +0 -34
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Requests
6
- class ResourceRead
7
- attr_reader :coordinator, :uri
8
-
9
- def initialize(coordinator, uri:)
10
- @coordinator = coordinator
11
- @uri = uri
12
- end
13
-
14
- def call
15
- coordinator.request(reading_resource_body(uri))
16
- end
17
-
18
- def reading_resource_body(uri)
19
- {
20
- jsonrpc: "2.0",
21
- method: "resources/read",
22
- params: {
23
- uri: uri
24
- }
25
- }
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Requests
6
- class ResourceTemplateList
7
- include Shared::Pagination
8
-
9
- def initialize(coordinator, cursor: nil)
10
- @coordinator = coordinator
11
- @cursor = cursor
12
- end
13
-
14
- def call
15
- body = merge_pagination(resource_template_list_body)
16
- @coordinator.request(body)
17
- end
18
-
19
- private
20
-
21
- def resource_template_list_body
22
- {
23
- jsonrpc: "2.0",
24
- method: "resources/templates/list",
25
- params: {}
26
- }
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Requests
6
- class ResourcesSubscribe
7
- def initialize(coordinator, uri:)
8
- @coordinator = coordinator
9
- @uri = uri
10
- end
11
-
12
- def call
13
- @coordinator.request(resources_subscribe_body, wait_for_response: false)
14
- end
15
-
16
- private
17
-
18
- def resources_subscribe_body
19
- {
20
- jsonrpc: "2.0",
21
- method: "resources/subscribe",
22
- params: {
23
- uri: @uri
24
- }
25
- }
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "securerandom"
4
-
5
- module RubyLLM
6
- module MCP
7
- module Requests
8
- module Shared
9
- module Meta
10
- def merge_meta(body)
11
- meta = {}
12
- meta.merge!(progress_token) if @coordinator.client.tracking_progress?
13
-
14
- body[:params] ||= {}
15
- body[:params].merge!({ _meta: meta }) unless meta.empty?
16
- body
17
- end
18
-
19
- private
20
-
21
- def progress_token
22
- { progressToken: generate_progress_token }
23
- end
24
-
25
- def generate_progress_token
26
- SecureRandom.uuid
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Requests
6
- module Shared
7
- module Pagination
8
- def merge_pagination(body)
9
- body[:params] ||= {}
10
- body[:params].merge!({ cursor: @cursor }) if @cursor
11
- body
12
- end
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Requests
6
- class ToolCall
7
- include Shared::Meta
8
-
9
- def initialize(coordinator, name:, parameters: {})
10
- @coordinator = coordinator
11
- @name = name
12
- @parameters = parameters
13
- end
14
-
15
- def call
16
- body = merge_meta(request_body)
17
- @coordinator.request(body)
18
- end
19
-
20
- private
21
-
22
- def request_body
23
- {
24
- jsonrpc: "2.0",
25
- method: "tools/call",
26
- params: {
27
- name: @name,
28
- arguments: @parameters
29
- }
30
- }
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Requests
6
- class ToolList
7
- include Shared::Pagination
8
-
9
- def initialize(coordinator, cursor: nil)
10
- @coordinator = coordinator
11
- @cursor = cursor
12
- end
13
-
14
- def call
15
- body = merge_pagination(tool_list_body)
16
- @coordinator.request(body)
17
- end
18
-
19
- private
20
-
21
- def tool_list_body
22
- {
23
- jsonrpc: "2.0",
24
- method: "tools/list",
25
- params: {}
26
- }
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- class ResponseHandler
6
- attr_reader :coordinator, :client
7
-
8
- def initialize(coordinator)
9
- @coordinator = coordinator
10
- @client = coordinator.client
11
- end
12
-
13
- def execute(result) # rubocop:disable Naming/PredicateMethod
14
- if result.ping?
15
- coordinator.ping_response(id: result.id)
16
- true
17
- elsif result.roots?
18
- handle_roots_response(result)
19
- true
20
- elsif result.sampling?
21
- handle_sampling_response(result)
22
- true
23
- elsif result.elicitation?
24
- handle_elicitation_response(result)
25
- true
26
- else
27
- handle_unknown_request(result)
28
- RubyLLM::MCP.logger.error("MCP client was sent unknown method type and could not respond: #{result.inspect}")
29
- false
30
- end
31
- end
32
-
33
- private
34
-
35
- def handle_roots_response(result)
36
- RubyLLM::MCP.logger.info("Roots request: #{result.inspect}")
37
- if client.roots.active?
38
- coordinator.roots_list_response(id: result.id, roots: client.roots)
39
- else
40
- coordinator.error_response(id: result.id, message: "Roots are not enabled", code: -32_000)
41
- end
42
- end
43
-
44
- def handle_sampling_response(result)
45
- unless MCP.config.sampling.enabled?
46
- RubyLLM::MCP.logger.info("Sampling is disabled, yet server requested sampling")
47
- coordinator.error_response(id: result.id, message: "Sampling is disabled", code: -32_000)
48
- return
49
- end
50
-
51
- RubyLLM::MCP.logger.info("Sampling request: #{result.inspect}")
52
- Sample.new(result, coordinator).execute
53
- end
54
-
55
- def handle_elicitation_response(result)
56
- RubyLLM::MCP.logger.info("Elicitation request: #{result.inspect}")
57
- Elicitation.new(coordinator, result).execute
58
- end
59
-
60
- def handle_unknown_request(result)
61
- coordinator.error_response(id: result.id,
62
- message: "Unknown method and could not respond: #{result.method}",
63
- code: -32_000)
64
- end
65
- end
66
- end
67
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Responses
6
- class Elicitation
7
- def initialize(coordinator, id:, action:, content:)
8
- @coordinator = coordinator
9
- @id = id
10
- @action = action
11
- @content = content
12
- end
13
-
14
- def call
15
- @coordinator.request(elicitation_response_body, add_id: false, wait_for_response: false)
16
- end
17
-
18
- private
19
-
20
- def elicitation_response_body
21
- {
22
- jsonrpc: "2.0",
23
- id: @id,
24
- result: {
25
- action: @action,
26
- content: @content
27
- }.compact
28
- }
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Responses
6
- class Error
7
- def initialize(coordinator, id:, message:, code: -32_000)
8
- @coordinator = coordinator
9
- @id = id
10
- @message = message
11
- @code = code
12
- end
13
-
14
- def call
15
- @coordinator.request(sampling_error_body, add_id: false, wait_for_response: false)
16
- end
17
-
18
- private
19
-
20
- def sampling_error_body
21
- {
22
- jsonrpc: "2.0",
23
- id: @id,
24
- error: {
25
- code: @code,
26
- message: @message
27
- }
28
- }
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Responses
6
- class Ping
7
- def initialize(coordinator, id:)
8
- @coordinator = coordinator
9
- @id = id
10
- end
11
-
12
- def call
13
- @coordinator.request(ping_response_body, add_id: false, wait_for_response: false)
14
- end
15
-
16
- private
17
-
18
- def ping_response_body
19
- {
20
- jsonrpc: "2.0",
21
- id: @id,
22
- result: {}
23
- }
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Responses
6
- class RootsList
7
- def initialize(coordinator, roots:, id:)
8
- @coordinator = coordinator
9
- @roots = roots
10
- @id = id
11
- end
12
-
13
- def call
14
- @coordinator.request(roots_list_body, add_id: false, wait_for_response: false)
15
- end
16
-
17
- private
18
-
19
- def roots_list_body
20
- {
21
- jsonrpc: "2.0",
22
- id: @id,
23
- result: {
24
- roots: @roots.to_request
25
- }
26
- }
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- module Responses
6
- class SamplingCreateMessage
7
- def initialize(coordinator, id:, message:, model:)
8
- @coordinator = coordinator
9
- @id = id
10
- @message = message
11
- @model = model
12
- end
13
-
14
- def call
15
- @coordinator.request(sampling_create_message_body, add_id: false, wait_for_response: false)
16
- end
17
-
18
- private
19
-
20
- def sampling_create_message_body
21
- {
22
- jsonrpc: "2.0",
23
- id: @id,
24
- result: {
25
- role: @message.role,
26
- content: format_content(@message.content),
27
- model: @model,
28
- # TODO: We are going to assume it was a endTurn
29
- # Look into getting RubyLLM to expose stopReason in message response
30
- stopReason: "endTurn"
31
- }
32
- }
33
- end
34
-
35
- def format_content(content)
36
- if content.is_a?(RubyLLM::Content)
37
- if context.text.none? && content.attachments.any?
38
- attachment = content.attachments.first
39
- { type: attachment.type, data: attachment.content, mime_type: attachment.mime_type }
40
- else
41
- { type: "text", text: content.text }
42
- end
43
- else
44
- { type: "text", text: content }
45
- end
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module MCP
5
- class Transport
6
- class << self
7
- def transports
8
- @transports ||= {}
9
- end
10
-
11
- def register_transport(transport_type, transport_class)
12
- transports[transport_type] = transport_class
13
- end
14
- end
15
-
16
- extend Forwardable
17
-
18
- register_transport(:sse, RubyLLM::MCP::Transports::SSE)
19
- register_transport(:stdio, RubyLLM::MCP::Transports::Stdio)
20
- register_transport(:streamable, RubyLLM::MCP::Transports::StreamableHTTP)
21
- register_transport(:streamable_http, RubyLLM::MCP::Transports::StreamableHTTP)
22
-
23
- attr_reader :transport_type, :coordinator, :config, :pid
24
-
25
- def initialize(transport_type, coordinator, config:)
26
- @transport_type = transport_type
27
- @coordinator = coordinator
28
- @config = config
29
- @pid = Process.pid
30
- end
31
-
32
- def_delegators :transport_protocol, :request, :alive?, :close, :start, :set_protocol_version
33
-
34
- def transport_protocol
35
- if @pid != Process.pid
36
- @pid = Process.pid
37
- @transport = build_transport
38
- coordinator.restart_transport
39
- end
40
-
41
- @transport_protocol ||= build_transport
42
- end
43
-
44
- private
45
-
46
- def build_transport
47
- unless RubyLLM::MCP::Transport.transports.key?(transport_type)
48
- supported_types = RubyLLM::MCP::Transport.transports.keys.join(", ")
49
- message = "Invalid transport type: :#{transport_type}. Supported types are #{supported_types}"
50
- raise Errors::InvalidTransportType.new(message: message)
51
- end
52
-
53
- transport_klass = RubyLLM::MCP::Transport.transports[transport_type]
54
- transport_klass.new(coordinator: coordinator, **config)
55
- end
56
- end
57
- end
58
- end