ruby_llm_swarm-mcp 0.8.0 → 0.8.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/README.md +3 -44
- data/lib/generators/ruby_llm/mcp/install/templates/initializer.rb +4 -21
- data/lib/generators/ruby_llm/mcp/install/templates/mcps.yml +0 -20
- data/lib/ruby_llm/mcp/auth/browser/http_server.rb +3 -0
- data/lib/ruby_llm/mcp/auth/browser/opener.rb +2 -0
- data/lib/ruby_llm/mcp/auth/browser/pages.rb +32 -100
- data/lib/ruby_llm/mcp/auth/browser_oauth_provider.rb +6 -32
- data/lib/ruby_llm/mcp/auth/http_response_handler.rb +2 -0
- data/lib/ruby_llm/mcp/auth/memory_storage.rb +0 -18
- data/lib/ruby_llm/mcp/auth/oauth_provider.rb +3 -82
- data/lib/ruby_llm/mcp/auth/session_manager.rb +2 -0
- data/lib/ruby_llm/mcp/auth/url_builder.rb +2 -0
- data/lib/ruby_llm/mcp/client.rb +32 -119
- data/lib/ruby_llm/mcp/configuration.rb +6 -74
- data/lib/ruby_llm/mcp/coordinator.rb +304 -0
- data/lib/ruby_llm/mcp/elicitation.rb +6 -8
- data/lib/ruby_llm/mcp/errors.rb +0 -15
- data/lib/ruby_llm/mcp/notification_handler.rb +5 -21
- data/lib/ruby_llm/mcp/notifications/cancelled.rb +32 -0
- data/lib/ruby_llm/mcp/notifications/initialize.rb +24 -0
- data/lib/ruby_llm/mcp/notifications/roots_list_change.rb +26 -0
- data/lib/ruby_llm/mcp/prompt.rb +7 -7
- data/lib/ruby_llm/mcp/protocol.rb +34 -0
- data/lib/ruby_llm/mcp/railtie.rb +6 -8
- data/lib/ruby_llm/mcp/requests/completion_prompt.rb +50 -0
- data/lib/ruby_llm/mcp/requests/completion_resource.rb +50 -0
- data/lib/ruby_llm/mcp/requests/initialization.rb +34 -0
- data/lib/ruby_llm/mcp/requests/logging_set_level.rb +28 -0
- data/lib/ruby_llm/mcp/requests/ping.rb +24 -0
- data/lib/ruby_llm/mcp/requests/prompt_call.rb +32 -0
- data/lib/ruby_llm/mcp/requests/prompt_list.rb +31 -0
- data/lib/ruby_llm/mcp/requests/resource_list.rb +31 -0
- data/lib/ruby_llm/mcp/requests/resource_read.rb +30 -0
- data/lib/ruby_llm/mcp/requests/resource_template_list.rb +31 -0
- data/lib/ruby_llm/mcp/requests/resources_subscribe.rb +30 -0
- data/lib/ruby_llm/mcp/requests/shared/meta.rb +32 -0
- data/lib/ruby_llm/mcp/requests/shared/pagination.rb +17 -0
- data/lib/ruby_llm/mcp/requests/tool_call.rb +35 -0
- data/lib/ruby_llm/mcp/requests/tool_list.rb +31 -0
- data/lib/ruby_llm/mcp/resource.rb +8 -6
- data/lib/ruby_llm/mcp/resource_template.rb +7 -7
- data/lib/ruby_llm/mcp/response_handler.rb +67 -0
- data/lib/ruby_llm/mcp/responses/elicitation.rb +33 -0
- data/lib/ruby_llm/mcp/responses/error.rb +33 -0
- data/lib/ruby_llm/mcp/responses/ping.rb +28 -0
- data/lib/ruby_llm/mcp/responses/roots_list.rb +31 -0
- data/lib/ruby_llm/mcp/responses/sampling_create_message.rb +50 -0
- data/lib/ruby_llm/mcp/result.rb +4 -8
- data/lib/ruby_llm/mcp/roots.rb +4 -4
- data/lib/ruby_llm/mcp/sample.rb +2 -6
- data/lib/ruby_llm/mcp/tool.rb +9 -9
- data/lib/ruby_llm/mcp/transport.rb +151 -0
- data/lib/ruby_llm/mcp/transports/sse.rb +435 -0
- data/lib/ruby_llm/mcp/transports/stdio.rb +231 -0
- data/lib/ruby_llm/mcp/transports/streamable_http.rb +725 -0
- data/lib/ruby_llm/mcp/transports/support/http_client.rb +28 -0
- data/lib/ruby_llm/mcp/transports/support/rate_limit.rb +47 -0
- data/lib/ruby_llm/mcp/transports/support/timeout.rb +34 -0
- data/lib/ruby_llm/mcp/version.rb +1 -1
- data/lib/ruby_llm/mcp.rb +7 -30
- metadata +38 -33
- data/lib/ruby_llm/mcp/adapters/base_adapter.rb +0 -179
- data/lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb +0 -292
- data/lib/ruby_llm/mcp/adapters/mcp_transports/coordinator_stub.rb +0 -33
- data/lib/ruby_llm/mcp/adapters/mcp_transports/sse.rb +0 -52
- data/lib/ruby_llm/mcp/adapters/mcp_transports/stdio.rb +0 -52
- data/lib/ruby_llm/mcp/adapters/mcp_transports/streamable_http.rb +0 -86
- data/lib/ruby_llm/mcp/adapters/ruby_llm_adapter.rb +0 -92
- data/lib/ruby_llm/mcp/auth/transport_oauth_helper.rb +0 -107
- data/lib/ruby_llm/mcp/native/cancellable_operation.rb +0 -57
- data/lib/ruby_llm/mcp/native/client.rb +0 -387
- data/lib/ruby_llm/mcp/native/json_rpc.rb +0 -170
- data/lib/ruby_llm/mcp/native/messages/helpers.rb +0 -39
- data/lib/ruby_llm/mcp/native/messages/notifications.rb +0 -42
- data/lib/ruby_llm/mcp/native/messages/requests.rb +0 -206
- data/lib/ruby_llm/mcp/native/messages/responses.rb +0 -106
- data/lib/ruby_llm/mcp/native/messages.rb +0 -36
- data/lib/ruby_llm/mcp/native/notification.rb +0 -16
- data/lib/ruby_llm/mcp/native/protocol.rb +0 -36
- data/lib/ruby_llm/mcp/native/response_handler.rb +0 -110
- data/lib/ruby_llm/mcp/native/transport.rb +0 -88
- data/lib/ruby_llm/mcp/native/transports/sse.rb +0 -607
- data/lib/ruby_llm/mcp/native/transports/stdio.rb +0 -356
- data/lib/ruby_llm/mcp/native/transports/streamable_http.rb +0 -926
- data/lib/ruby_llm/mcp/native/transports/support/http_client.rb +0 -28
- data/lib/ruby_llm/mcp/native/transports/support/rate_limit.rb +0 -49
- data/lib/ruby_llm/mcp/native/transports/support/timeout.rb +0 -36
- data/lib/ruby_llm/mcp/native.rb +0 -12
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module MCP
|
|
5
|
+
module Requests
|
|
6
|
+
class Initialization
|
|
7
|
+
def initialize(coordinator)
|
|
8
|
+
@coordinator = coordinator
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
@coordinator.request(initialize_body)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def initialize_body
|
|
18
|
+
{
|
|
19
|
+
jsonrpc: "2.0",
|
|
20
|
+
method: "initialize",
|
|
21
|
+
params: {
|
|
22
|
+
protocolVersion: @coordinator.protocol_version,
|
|
23
|
+
capabilities: @coordinator.client_capabilities,
|
|
24
|
+
clientInfo: {
|
|
25
|
+
name: "RubyLLM-MCP Client",
|
|
26
|
+
version: RubyLLM::MCP::VERSION
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module MCP
|
|
5
|
+
module Requests
|
|
6
|
+
class LoggingSetLevel
|
|
7
|
+
def initialize(coordinator, level:)
|
|
8
|
+
@coordinator = coordinator
|
|
9
|
+
@level = level
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
@coordinator.request(logging_set_body)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def logging_set_body
|
|
17
|
+
{
|
|
18
|
+
jsonrpc: "2.0",
|
|
19
|
+
method: "logging/setLevel",
|
|
20
|
+
params: {
|
|
21
|
+
level: @level
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module MCP
|
|
5
|
+
module Requests
|
|
6
|
+
class Ping
|
|
7
|
+
def initialize(coordinator)
|
|
8
|
+
@coordinator = coordinator
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
@coordinator.request(ping_body)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ping_body
|
|
16
|
+
{
|
|
17
|
+
jsonrpc: "2.0",
|
|
18
|
+
method: "ping"
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module MCP
|
|
5
|
+
module Requests
|
|
6
|
+
class PromptCall
|
|
7
|
+
def initialize(coordinator, name:, arguments: {})
|
|
8
|
+
@coordinator = coordinator
|
|
9
|
+
@name = name
|
|
10
|
+
@arguments = arguments
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
@coordinator.request(request_body)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def request_body
|
|
20
|
+
{
|
|
21
|
+
jsonrpc: "2.0",
|
|
22
|
+
method: "prompts/get",
|
|
23
|
+
params: {
|
|
24
|
+
name: @name,
|
|
25
|
+
arguments: @arguments
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module MCP
|
|
5
|
+
module Requests
|
|
6
|
+
class PromptList
|
|
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(request_body)
|
|
16
|
+
@coordinator.request(body)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def request_body
|
|
22
|
+
{
|
|
23
|
+
jsonrpc: "2.0",
|
|
24
|
+
method: "prompts/list",
|
|
25
|
+
params: {}
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module MCP
|
|
5
|
+
module Requests
|
|
6
|
+
class ResourceList
|
|
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_list_body)
|
|
16
|
+
@coordinator.request(body)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def resource_list_body
|
|
22
|
+
{
|
|
23
|
+
jsonrpc: "2.0",
|
|
24
|
+
method: "resources/list",
|
|
25
|
+
params: {}
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
|
@@ -0,0 +1,31 @@
|
|
|
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,12 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "httpx"
|
|
4
|
+
|
|
3
5
|
module RubyLLM
|
|
4
6
|
module MCP
|
|
5
7
|
class Resource
|
|
6
|
-
attr_reader :uri, :name, :description, :mime_type, :
|
|
8
|
+
attr_reader :uri, :name, :description, :mime_type, :coordinator, :subscribed
|
|
7
9
|
|
|
8
|
-
def initialize(
|
|
9
|
-
@
|
|
10
|
+
def initialize(coordinator, resource)
|
|
11
|
+
@coordinator = coordinator
|
|
10
12
|
@uri = resource["uri"]
|
|
11
13
|
@name = resource["name"]
|
|
12
14
|
@description = resource["description"]
|
|
@@ -34,8 +36,8 @@ module RubyLLM
|
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def subscribe!
|
|
37
|
-
if @
|
|
38
|
-
@
|
|
39
|
+
if @coordinator.capabilities.resource_subscribe?
|
|
40
|
+
@coordinator.resources_subscribe(uri: @uri)
|
|
39
41
|
@subscribed = true
|
|
40
42
|
else
|
|
41
43
|
message = "Resource subscribe is not available for this MCP server"
|
|
@@ -99,7 +101,7 @@ module RubyLLM
|
|
|
99
101
|
when "http", "https"
|
|
100
102
|
fetch_uri_content(uri)
|
|
101
103
|
else # file:// or git://
|
|
102
|
-
@
|
|
104
|
+
@coordinator.resource_read(uri: uri)
|
|
103
105
|
end
|
|
104
106
|
end
|
|
105
107
|
|
|
@@ -5,10 +5,10 @@ require "httpx"
|
|
|
5
5
|
module RubyLLM
|
|
6
6
|
module MCP
|
|
7
7
|
class ResourceTemplate
|
|
8
|
-
attr_reader :uri, :name, :description, :mime_type, :
|
|
8
|
+
attr_reader :uri, :name, :description, :mime_type, :coordinator, :template
|
|
9
9
|
|
|
10
|
-
def initialize(
|
|
11
|
-
@
|
|
10
|
+
def initialize(coordinator, resource)
|
|
11
|
+
@coordinator = coordinator
|
|
12
12
|
@uri = resource["uriTemplate"]
|
|
13
13
|
@name = resource["name"]
|
|
14
14
|
@description = resource["description"]
|
|
@@ -20,7 +20,7 @@ module RubyLLM
|
|
|
20
20
|
result = read_response(uri)
|
|
21
21
|
content_response = result.value.dig("contents", 0)
|
|
22
22
|
|
|
23
|
-
Resource.new(
|
|
23
|
+
Resource.new(coordinator, {
|
|
24
24
|
"uri" => uri,
|
|
25
25
|
"name" => "#{@name} (#{uri})",
|
|
26
26
|
"description" => @description,
|
|
@@ -34,8 +34,8 @@ module RubyLLM
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def complete(argument, value, context: nil)
|
|
37
|
-
if @
|
|
38
|
-
result = @
|
|
37
|
+
if @coordinator.capabilities.completion?
|
|
38
|
+
result = @coordinator.completion_resource(uri: @uri, argument: argument, value: value, context: context)
|
|
39
39
|
result.raise_error! if result.error?
|
|
40
40
|
|
|
41
41
|
response = result.value["completion"]
|
|
@@ -64,7 +64,7 @@ module RubyLLM
|
|
|
64
64
|
when "http", "https"
|
|
65
65
|
fetch_uri_content(uri)
|
|
66
66
|
else # file:// or git://
|
|
67
|
-
@
|
|
67
|
+
@coordinator.resource_read(uri: uri)
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -0,0 +1,67 @@
|
|
|
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
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|