sc-ruby_llm-mcp 0.3.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +446 -0
- data/lib/ruby_llm/chat.rb +33 -0
- data/lib/ruby_llm/mcp/attachment.rb +18 -0
- data/lib/ruby_llm/mcp/capabilities.rb +29 -0
- data/lib/ruby_llm/mcp/client.rb +104 -0
- data/lib/ruby_llm/mcp/completion.rb +15 -0
- data/lib/ruby_llm/mcp/content.rb +20 -0
- data/lib/ruby_llm/mcp/coordinator.rb +112 -0
- data/lib/ruby_llm/mcp/errors.rb +28 -0
- data/lib/ruby_llm/mcp/parameter.rb +19 -0
- data/lib/ruby_llm/mcp/prompt.rb +106 -0
- data/lib/ruby_llm/mcp/providers/anthropic/complex_parameter_support.rb +65 -0
- data/lib/ruby_llm/mcp/providers/gemini/complex_parameter_support.rb +61 -0
- data/lib/ruby_llm/mcp/providers/openai/complex_parameter_support.rb +52 -0
- data/lib/ruby_llm/mcp/requests/base.rb +31 -0
- data/lib/ruby_llm/mcp/requests/completion_prompt.rb +40 -0
- data/lib/ruby_llm/mcp/requests/completion_resource.rb +40 -0
- data/lib/ruby_llm/mcp/requests/initialization.rb +24 -0
- data/lib/ruby_llm/mcp/requests/initialize_notification.rb +14 -0
- data/lib/ruby_llm/mcp/requests/prompt_call.rb +32 -0
- data/lib/ruby_llm/mcp/requests/prompt_list.rb +23 -0
- data/lib/ruby_llm/mcp/requests/resource_list.rb +21 -0
- data/lib/ruby_llm/mcp/requests/resource_read.rb +30 -0
- data/lib/ruby_llm/mcp/requests/resource_template_list.rb +21 -0
- data/lib/ruby_llm/mcp/requests/tool_call.rb +32 -0
- data/lib/ruby_llm/mcp/requests/tool_list.rb +17 -0
- data/lib/ruby_llm/mcp/resource.rb +77 -0
- data/lib/ruby_llm/mcp/resource_template.rb +79 -0
- data/lib/ruby_llm/mcp/tool.rb +115 -0
- data/lib/ruby_llm/mcp/transport/sse.rb +244 -0
- data/lib/ruby_llm/mcp/transport/stdio.rb +210 -0
- data/lib/ruby_llm/mcp/transport/streamable.rb +299 -0
- data/lib/ruby_llm/mcp/version.rb +7 -0
- data/lib/ruby_llm/mcp.rb +27 -0
- metadata +175 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
class Coordinator
|
6
|
+
PROTOCOL_VERSION = "2025-03-26"
|
7
|
+
PV_2024_11_05 = "2024-11-05"
|
8
|
+
|
9
|
+
attr_reader :client, :transport_type, :config, :request_timeout, :headers, :transport, :initialize_response,
|
10
|
+
:capabilities, :protocol_version
|
11
|
+
|
12
|
+
def initialize(client, transport_type:, config: {})
|
13
|
+
@client = client
|
14
|
+
@transport_type = transport_type
|
15
|
+
@config = config
|
16
|
+
|
17
|
+
@protocol_version = PROTOCOL_VERSION
|
18
|
+
@headers = config[:headers] || {}
|
19
|
+
|
20
|
+
@transport = nil
|
21
|
+
@capabilities = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def request(body, **options)
|
25
|
+
@transport.request(body, **options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def start_transport
|
29
|
+
case @transport_type
|
30
|
+
when :sse
|
31
|
+
@transport = RubyLLM::MCP::Transport::SSE.new(@config[:url],
|
32
|
+
request_timeout: @config[:request_timeout],
|
33
|
+
headers: @headers)
|
34
|
+
when :stdio
|
35
|
+
@transport = RubyLLM::MCP::Transport::Stdio.new(@config[:command],
|
36
|
+
request_timeout: @config[:request_timeout],
|
37
|
+
args: @config[:args],
|
38
|
+
env: @config[:env])
|
39
|
+
when :streamable
|
40
|
+
@transport = RubyLLM::MCP::Transport::Streamable.new(@config[:url],
|
41
|
+
request_timeout: @config[:request_timeout],
|
42
|
+
headers: @headers)
|
43
|
+
else
|
44
|
+
message = "Invalid transport type: :#{transport_type}. Supported types are :sse, :stdio, :streamable"
|
45
|
+
raise Errors::InvalidTransportType.new(message: message)
|
46
|
+
end
|
47
|
+
|
48
|
+
@initialize_response = initialize_request
|
49
|
+
@capabilities = RubyLLM::MCP::Capabilities.new(@initialize_response["result"]["capabilities"])
|
50
|
+
initialize_notification
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop_transport
|
54
|
+
@transport&.close
|
55
|
+
@transport = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def restart_transport
|
59
|
+
stop_transport
|
60
|
+
start_transport
|
61
|
+
end
|
62
|
+
|
63
|
+
def alive?
|
64
|
+
!!@transport&.alive?
|
65
|
+
end
|
66
|
+
|
67
|
+
def execute_tool(**args)
|
68
|
+
RubyLLM::MCP::Requests::ToolCall.new(self, **args).call
|
69
|
+
end
|
70
|
+
|
71
|
+
def resource_read(**args)
|
72
|
+
RubyLLM::MCP::Requests::ResourceRead.new(self, **args).call
|
73
|
+
end
|
74
|
+
|
75
|
+
def completion_resource(**args)
|
76
|
+
RubyLLM::MCP::Requests::CompletionResource.new(self, **args).call
|
77
|
+
end
|
78
|
+
|
79
|
+
def completion_prompt(**args)
|
80
|
+
RubyLLM::MCP::Requests::CompletionPrompt.new(self, **args).call
|
81
|
+
end
|
82
|
+
|
83
|
+
def execute_prompt(**args)
|
84
|
+
RubyLLM::MCP::Requests::PromptCall.new(self, **args).call
|
85
|
+
end
|
86
|
+
|
87
|
+
def initialize_request
|
88
|
+
RubyLLM::MCP::Requests::Initialization.new(self).call
|
89
|
+
end
|
90
|
+
|
91
|
+
def initialize_notification
|
92
|
+
RubyLLM::MCP::Requests::InitializeNotification.new(self).call
|
93
|
+
end
|
94
|
+
|
95
|
+
def tool_list
|
96
|
+
RubyLLM::MCP::Requests::ToolList.new(self).call
|
97
|
+
end
|
98
|
+
|
99
|
+
def resource_list
|
100
|
+
RubyLLM::MCP::Requests::ResourceList.new(self).call
|
101
|
+
end
|
102
|
+
|
103
|
+
def resource_template_list
|
104
|
+
RubyLLM::MCP::Requests::ResourceTemplateList.new(self).call
|
105
|
+
end
|
106
|
+
|
107
|
+
def prompt_list
|
108
|
+
RubyLLM::MCP::Requests::PromptList.new(self).call
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
module Errors
|
6
|
+
class BaseError < StandardError
|
7
|
+
attr_reader :message
|
8
|
+
|
9
|
+
def initialize(message:)
|
10
|
+
@message = message
|
11
|
+
super(message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class CompletionNotAvailable < BaseError; end
|
16
|
+
|
17
|
+
class PromptArgumentError < BaseError; end
|
18
|
+
|
19
|
+
class InvalidProtocolVersionError < BaseError; end
|
20
|
+
|
21
|
+
class SessionExpiredError < BaseError; end
|
22
|
+
|
23
|
+
class TimeoutError < BaseError; end
|
24
|
+
|
25
|
+
class InvalidTransportType < BaseError; end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
class Parameter < RubyLLM::Parameter
|
6
|
+
attr_accessor :items, :properties, :enum, :union_type
|
7
|
+
|
8
|
+
def initialize(name, type: "string", desc: nil, required: true, union_type: nil)
|
9
|
+
super(name, type: type.to_sym, desc: desc, required: required)
|
10
|
+
@properties = {}
|
11
|
+
@union_type = union_type
|
12
|
+
end
|
13
|
+
|
14
|
+
def item_type
|
15
|
+
@items["type"].to_sym
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
class Prompt
|
6
|
+
class Argument
|
7
|
+
attr_reader :name, :description, :required
|
8
|
+
|
9
|
+
def initialize(name:, description:, required:)
|
10
|
+
@name = name
|
11
|
+
@description = description
|
12
|
+
@required = required
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :name, :description, :arguments, :coordinator
|
17
|
+
|
18
|
+
def initialize(coordinator, prompt)
|
19
|
+
@coordinator = coordinator
|
20
|
+
@name = prompt["name"]
|
21
|
+
@description = prompt["description"]
|
22
|
+
@arguments = parse_arguments(prompt["arguments"])
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch(arguments = {})
|
26
|
+
fetch_prompt_messages(arguments)
|
27
|
+
end
|
28
|
+
|
29
|
+
def include(chat, arguments: {})
|
30
|
+
validate_arguments!(arguments)
|
31
|
+
messages = fetch_prompt_messages(arguments)
|
32
|
+
|
33
|
+
messages.each { |message| chat.add_message(message) }
|
34
|
+
chat
|
35
|
+
end
|
36
|
+
|
37
|
+
def ask(chat, arguments: {}, &)
|
38
|
+
include(chat, arguments: arguments)
|
39
|
+
|
40
|
+
chat.complete(&)
|
41
|
+
end
|
42
|
+
|
43
|
+
alias say ask
|
44
|
+
|
45
|
+
def complete(argument, value)
|
46
|
+
if @coordinator.capabilities.completion?
|
47
|
+
response = @coordinator.completion_prompt(name: @name, argument: argument, value: value)
|
48
|
+
response = response.dig("result", "completion")
|
49
|
+
|
50
|
+
Completion.new(values: response["values"], total: response["total"], has_more: response["hasMore"])
|
51
|
+
else
|
52
|
+
raise Errors::CompletionNotAvailable.new(message: "Completion is not available for this MCP server")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def fetch_prompt_messages(arguments)
|
59
|
+
response = @coordinator.execute_prompt(
|
60
|
+
name: @name,
|
61
|
+
arguments: arguments
|
62
|
+
)
|
63
|
+
|
64
|
+
response["result"]["messages"].map do |message|
|
65
|
+
content = create_content_for_message(message["content"])
|
66
|
+
|
67
|
+
RubyLLM::Message.new(
|
68
|
+
role: message["role"],
|
69
|
+
content: content
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_arguments!(incoming_arguments)
|
75
|
+
@arguments.each do |arg|
|
76
|
+
if arg.required && incoming_arguments.key?(arg.name)
|
77
|
+
raise Errors::PromptArgumentError, "Argument #{arg.name} is required"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def create_content_for_message(content)
|
83
|
+
case content["type"]
|
84
|
+
when "text"
|
85
|
+
MCP::Content.new(text: content["text"])
|
86
|
+
when "image", "audio"
|
87
|
+
attachment = MCP::Attachment.new(content["content"], content["mime_type"])
|
88
|
+
MCP::Content.new(text: nil, attachments: [attachment])
|
89
|
+
when "resource"
|
90
|
+
resource = Resource.new(coordinator, content["resource"])
|
91
|
+
resource.to_content
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_arguments(arguments)
|
96
|
+
if arguments.nil?
|
97
|
+
[]
|
98
|
+
else
|
99
|
+
arguments.map do |arg|
|
100
|
+
Argument.new(name: arg["name"], description: arg["description"], required: arg["required"])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,65 @@
|
|
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
|
+
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 build_properties(param)
|
21
|
+
case param.type
|
22
|
+
when :array
|
23
|
+
if param.item_type == :object
|
24
|
+
{
|
25
|
+
type: param.type,
|
26
|
+
items: { type: param.item_type, properties: clean_parameters(param.properties) }
|
27
|
+
}
|
28
|
+
else
|
29
|
+
{
|
30
|
+
type: param.type,
|
31
|
+
items: { type: param.item_type, enum: param.enum }.compact
|
32
|
+
}
|
33
|
+
end
|
34
|
+
when :object
|
35
|
+
{
|
36
|
+
type: param.type,
|
37
|
+
properties: clean_parameters(param.properties),
|
38
|
+
required: required_parameters(param.properties)
|
39
|
+
}
|
40
|
+
when :union
|
41
|
+
{
|
42
|
+
param.union_type => param.properties.map { |property| build_properties(property) }
|
43
|
+
}
|
44
|
+
else
|
45
|
+
{
|
46
|
+
type: param.type,
|
47
|
+
description: param.description
|
48
|
+
}.compact
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module RubyLLM::Providers::Anthropic::Tools
|
58
|
+
def self.clean_parameters(parameters)
|
59
|
+
RubyLLM::MCP::Providers::Anthropic::ComplexParameterSupport.clean_parameters(parameters)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.required_parameters(parameters)
|
63
|
+
RubyLLM::MCP::Providers::Anthropic::ComplexParameterSupport.required_parameters(parameters)
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,61 @@
|
|
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| build_properties(param) },
|
15
|
+
required: parameters.select { |_, p| p.required }.keys.map(&:to_s)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_properties(param)
|
20
|
+
properties = case param.type
|
21
|
+
when :array
|
22
|
+
if param.item_type == :object
|
23
|
+
{
|
24
|
+
type: param_type_for_gemini(param.type),
|
25
|
+
items: {
|
26
|
+
type: param_type_for_gemini(param.item_type),
|
27
|
+
properties: param.properties.transform_values { |value| build_properties(value) }
|
28
|
+
}
|
29
|
+
}
|
30
|
+
else
|
31
|
+
{
|
32
|
+
type: param_type_for_gemini(param.type),
|
33
|
+
items: { type: param_type_for_gemini(param.item_type), enum: param.enum }.compact
|
34
|
+
}
|
35
|
+
end
|
36
|
+
when :object
|
37
|
+
{
|
38
|
+
type: param_type_for_gemini(param.type),
|
39
|
+
properties: param.properties.transform_values { |value| build_properties(value) },
|
40
|
+
required: param.properties.select { |_, p| p.required }.keys
|
41
|
+
}
|
42
|
+
when :union
|
43
|
+
{
|
44
|
+
param.union_type => param.properties.map { |properties| build_properties(properties) }
|
45
|
+
}
|
46
|
+
else
|
47
|
+
{
|
48
|
+
type: param_type_for_gemini(param.type),
|
49
|
+
description: param.description
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
properties.compact
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
RubyLLM::Providers::Gemini.extend(RubyLLM::MCP::Providers::Gemini::ComplexParameterSupport)
|
@@ -0,0 +1,52 @@
|
|
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)
|
11
|
+
properties = case param.type
|
12
|
+
when :array
|
13
|
+
if param.item_type == :object
|
14
|
+
{
|
15
|
+
type: param.type,
|
16
|
+
items: {
|
17
|
+
type: param.item_type,
|
18
|
+
properties: param.properties.transform_values { |value| param_schema(value) }
|
19
|
+
}
|
20
|
+
}
|
21
|
+
else
|
22
|
+
{
|
23
|
+
type: param.type,
|
24
|
+
items: { type: param.item_type, enum: param.enum }.compact
|
25
|
+
}
|
26
|
+
end
|
27
|
+
when :object
|
28
|
+
{
|
29
|
+
type: param.type,
|
30
|
+
properties: param.properties.transform_values { |value| param_schema(value) },
|
31
|
+
required: param.properties.select { |_, p| p.required }.keys
|
32
|
+
}
|
33
|
+
when :union
|
34
|
+
{
|
35
|
+
param.union_type => param.properties.map { |property| param_schema(property) }
|
36
|
+
}
|
37
|
+
else
|
38
|
+
{
|
39
|
+
type: param.type,
|
40
|
+
description: param.description
|
41
|
+
}.compact
|
42
|
+
end
|
43
|
+
|
44
|
+
properties.compact
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
RubyLLM::Providers::OpenAI.extend(RubyLLM::MCP::Providers::OpenAI::ComplexParameterSupport)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module RubyLLM
|
6
|
+
module MCP
|
7
|
+
module Requests
|
8
|
+
class Base
|
9
|
+
attr_reader :client
|
10
|
+
|
11
|
+
def initialize(client)
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
raise "Not implemented"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_response!(response, body)
|
22
|
+
# TODO: Implement response validation
|
23
|
+
end
|
24
|
+
|
25
|
+
def raise_error(error)
|
26
|
+
raise "MCP Error: code: #{error['code']} message: #{error['message']} data: #{error['data']}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
module Requests
|
6
|
+
class CompletionPrompt
|
7
|
+
def initialize(client, name:, argument:, value:)
|
8
|
+
@client = client
|
9
|
+
@name = name
|
10
|
+
@argument = argument
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
@client.request(request_body)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def request_body
|
21
|
+
{
|
22
|
+
jsonrpc: "2.0",
|
23
|
+
id: 1,
|
24
|
+
method: "completion/complete",
|
25
|
+
params: {
|
26
|
+
ref: {
|
27
|
+
type: "ref/prompt",
|
28
|
+
name: @name
|
29
|
+
},
|
30
|
+
argument: {
|
31
|
+
name: @argument,
|
32
|
+
value: @value
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
module Requests
|
6
|
+
class CompletionResource
|
7
|
+
def initialize(client, uri:, argument:, value:)
|
8
|
+
@client = client
|
9
|
+
@uri = uri
|
10
|
+
@argument = argument
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
@client.request(request_body)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def request_body
|
21
|
+
{
|
22
|
+
jsonrpc: "2.0",
|
23
|
+
id: 1,
|
24
|
+
method: "completion/complete",
|
25
|
+
params: {
|
26
|
+
ref: {
|
27
|
+
type: "ref/resource",
|
28
|
+
uri: @uri
|
29
|
+
},
|
30
|
+
argument: {
|
31
|
+
name: @argument,
|
32
|
+
value: @value
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RubyLLM::MCP::Requests::Initialization < RubyLLM::MCP::Requests::Base
|
4
|
+
def call
|
5
|
+
client.request(initialize_body)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def initialize_body
|
11
|
+
{
|
12
|
+
jsonrpc: "2.0",
|
13
|
+
method: "initialize",
|
14
|
+
params: {
|
15
|
+
protocolVersion: @client.protocol_version,
|
16
|
+
capabilities: {},
|
17
|
+
clientInfo: {
|
18
|
+
name: "RubyLLM-MCP Client",
|
19
|
+
version: RubyLLM::MCP::VERSION
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RubyLLM::MCP::Requests::InitializeNotification < RubyLLM::MCP::Requests::Base
|
4
|
+
def call
|
5
|
+
client.request(notification_body, add_id: false, wait_for_response: false)
|
6
|
+
end
|
7
|
+
|
8
|
+
def notification_body
|
9
|
+
{
|
10
|
+
jsonrpc: "2.0",
|
11
|
+
method: "notifications/initialized"
|
12
|
+
}
|
13
|
+
end
|
14
|
+
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(client, name:, arguments: {})
|
8
|
+
@client = client
|
9
|
+
@name = name
|
10
|
+
@arguments = arguments
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
@client.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,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
module Requests
|
6
|
+
class PromptList < Base
|
7
|
+
def call
|
8
|
+
client.request(request_body)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def request_body
|
14
|
+
{
|
15
|
+
jsonrpc: "2.0",
|
16
|
+
method: "prompts/list",
|
17
|
+
params: {}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module MCP
|
5
|
+
module Requests
|
6
|
+
class ResourceList < Base
|
7
|
+
def call
|
8
|
+
client.request(resource_list_body)
|
9
|
+
end
|
10
|
+
|
11
|
+
def resource_list_body
|
12
|
+
{
|
13
|
+
jsonrpc: "2.0",
|
14
|
+
method: "resources/list",
|
15
|
+
params: {}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|