model-context-protocol-rb 0.3.4 → 0.4.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/CHANGELOG.md +18 -1
- data/README.md +745 -198
- data/lib/model_context_protocol/server/configuration.rb +79 -2
- data/lib/model_context_protocol/server/content.rb +321 -0
- data/lib/model_context_protocol/server/content_helpers.rb +84 -0
- data/lib/model_context_protocol/server/pagination.rb +71 -0
- data/lib/model_context_protocol/server/prompt.rb +106 -14
- data/lib/model_context_protocol/server/registry.rb +94 -18
- data/lib/model_context_protocol/server/resource.rb +95 -25
- data/lib/model_context_protocol/server/resource_template.rb +26 -13
- data/lib/model_context_protocol/server/streamable_http_transport.rb +211 -54
- data/lib/model_context_protocol/server/tool.rb +75 -53
- data/lib/model_context_protocol/server.rb +115 -18
- data/lib/model_context_protocol/version.rb +1 -1
- data/tasks/mcp.rake +28 -2
- data/tasks/templates/dev-http.erb +244 -0
- data/tasks/templates/dev.erb +7 -1
- metadata +6 -2
@@ -1,5 +1,7 @@
|
|
1
1
|
module ModelContextProtocol
|
2
2
|
class Server::Prompt
|
3
|
+
include Server::ContentHelpers
|
4
|
+
|
3
5
|
attr_reader :arguments, :context, :logger
|
4
6
|
|
5
7
|
def initialize(arguments, logger, context = {})
|
@@ -13,15 +15,23 @@ module ModelContextProtocol
|
|
13
15
|
raise NotImplementedError, "Subclasses must implement the call method"
|
14
16
|
end
|
15
17
|
|
16
|
-
Response = Data.define(:messages, :description) do
|
18
|
+
Response = Data.define(:messages, :description, :title) do
|
17
19
|
def serialized
|
18
|
-
{description:, messages:}
|
20
|
+
result = {description:, messages:}
|
21
|
+
result[:title] = title if title
|
22
|
+
result
|
19
23
|
end
|
20
24
|
end
|
21
25
|
private_constant :Response
|
22
26
|
|
27
|
+
def message_history(&block)
|
28
|
+
builder = MessageHistoryBuilder.new(self)
|
29
|
+
builder.instance_eval(&block)
|
30
|
+
builder.messages
|
31
|
+
end
|
32
|
+
|
23
33
|
private def respond_with(messages:)
|
24
|
-
Response[messages:, description: self.class.description]
|
34
|
+
Response[messages:, description: self.class.description, title: self.class.title]
|
25
35
|
end
|
26
36
|
|
27
37
|
private def validate!(arguments = {})
|
@@ -43,16 +53,18 @@ module ModelContextProtocol
|
|
43
53
|
end
|
44
54
|
|
45
55
|
class << self
|
46
|
-
attr_reader :name, :description, :defined_arguments
|
56
|
+
attr_reader :name, :description, :title, :defined_arguments
|
47
57
|
|
48
|
-
def
|
58
|
+
def define(&block)
|
49
59
|
@defined_arguments ||= []
|
50
60
|
|
51
|
-
|
52
|
-
|
61
|
+
definition_dsl = DefinitionDSL.new
|
62
|
+
definition_dsl.instance_eval(&block)
|
53
63
|
|
54
|
-
@name =
|
55
|
-
@description =
|
64
|
+
@name = definition_dsl.name
|
65
|
+
@description = definition_dsl.description
|
66
|
+
@title = definition_dsl.title
|
67
|
+
@defined_arguments.concat(definition_dsl.arguments)
|
56
68
|
end
|
57
69
|
|
58
70
|
def with_argument(&block)
|
@@ -72,6 +84,7 @@ module ModelContextProtocol
|
|
72
84
|
def inherited(subclass)
|
73
85
|
subclass.instance_variable_set(:@name, @name)
|
74
86
|
subclass.instance_variable_set(:@description, @description)
|
87
|
+
subclass.instance_variable_set(:@title, @title)
|
75
88
|
subclass.instance_variable_set(:@defined_arguments, @defined_arguments&.dup)
|
76
89
|
end
|
77
90
|
|
@@ -81,8 +94,10 @@ module ModelContextProtocol
|
|
81
94
|
raise ModelContextProtocol::Server::ParameterValidationError, error.message
|
82
95
|
end
|
83
96
|
|
84
|
-
def
|
85
|
-
{name: @name, description: @description, arguments: @defined_arguments}
|
97
|
+
def definition
|
98
|
+
result = {name: @name, description: @description, arguments: @defined_arguments}
|
99
|
+
result[:title] = @title if @title
|
100
|
+
result
|
86
101
|
end
|
87
102
|
|
88
103
|
def complete_for(arg_name, value)
|
@@ -92,7 +107,52 @@ module ModelContextProtocol
|
|
92
107
|
end
|
93
108
|
end
|
94
109
|
|
95
|
-
class
|
110
|
+
class MessageHistoryBuilder
|
111
|
+
include Server::ContentHelpers
|
112
|
+
|
113
|
+
attr_reader :messages
|
114
|
+
|
115
|
+
def initialize(prompt_instance)
|
116
|
+
@messages = []
|
117
|
+
@prompt_instance = prompt_instance
|
118
|
+
end
|
119
|
+
|
120
|
+
def arguments
|
121
|
+
@prompt_instance.arguments
|
122
|
+
end
|
123
|
+
|
124
|
+
def context
|
125
|
+
@prompt_instance.context
|
126
|
+
end
|
127
|
+
|
128
|
+
def logger
|
129
|
+
@prompt_instance.logger
|
130
|
+
end
|
131
|
+
|
132
|
+
def user_message(&block)
|
133
|
+
content = instance_eval(&block).serialized
|
134
|
+
@messages << {
|
135
|
+
role: "user",
|
136
|
+
content: content
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
def assistant_message(&block)
|
141
|
+
content = instance_eval(&block).serialized
|
142
|
+
@messages << {
|
143
|
+
role: "assistant",
|
144
|
+
content: content
|
145
|
+
}
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class DefinitionDSL
|
150
|
+
attr_reader :arguments
|
151
|
+
|
152
|
+
def initialize
|
153
|
+
@arguments = []
|
154
|
+
end
|
155
|
+
|
96
156
|
def name(value = nil)
|
97
157
|
@name = value if value
|
98
158
|
@name
|
@@ -102,6 +162,23 @@ module ModelContextProtocol
|
|
102
162
|
@description = value if value
|
103
163
|
@description
|
104
164
|
end
|
165
|
+
|
166
|
+
def title(value = nil)
|
167
|
+
@title = value if value
|
168
|
+
@title
|
169
|
+
end
|
170
|
+
|
171
|
+
def argument(&block)
|
172
|
+
argument_dsl = ArgumentDSL.new
|
173
|
+
argument_dsl.instance_eval(&block)
|
174
|
+
|
175
|
+
@arguments << {
|
176
|
+
name: argument_dsl.name,
|
177
|
+
description: argument_dsl.description,
|
178
|
+
required: argument_dsl.required,
|
179
|
+
completion: argument_dsl.completion
|
180
|
+
}
|
181
|
+
end
|
105
182
|
end
|
106
183
|
|
107
184
|
class ArgumentDSL
|
@@ -120,10 +197,25 @@ module ModelContextProtocol
|
|
120
197
|
@required
|
121
198
|
end
|
122
199
|
|
123
|
-
def completion(
|
124
|
-
|
200
|
+
def completion(klass_or_values = nil)
|
201
|
+
unless klass_or_values.nil?
|
202
|
+
@completion = if klass_or_values.is_a?(Array)
|
203
|
+
create_array_completion(klass_or_values)
|
204
|
+
else
|
205
|
+
klass_or_values
|
206
|
+
end
|
207
|
+
end
|
125
208
|
@completion
|
126
209
|
end
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
def create_array_completion(values)
|
214
|
+
ModelContextProtocol::Server::Completion.define do
|
215
|
+
filtered_values = values.grep(/#{argument_value}/)
|
216
|
+
respond_with values: filtered_values
|
217
|
+
end
|
218
|
+
end
|
127
219
|
end
|
128
220
|
end
|
129
221
|
end
|
@@ -39,8 +39,8 @@ module ModelContextProtocol
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def register(klass)
|
42
|
-
|
43
|
-
entry = {klass: klass}.merge(
|
42
|
+
definition = klass.definition
|
43
|
+
entry = {klass: klass}.merge(definition)
|
44
44
|
|
45
45
|
case klass.ancestors
|
46
46
|
when ->(ancestors) { ancestors.include?(ModelContextProtocol::Server::Prompt) }
|
@@ -74,45 +74,121 @@ module ModelContextProtocol
|
|
74
74
|
find_by_name(@tools, name)
|
75
75
|
end
|
76
76
|
|
77
|
-
def prompts_data
|
78
|
-
|
77
|
+
def prompts_data(cursor: nil, page_size: nil, cursor_ttl: nil)
|
78
|
+
items = @prompts.map { |entry| entry.except(:klass) }
|
79
|
+
|
80
|
+
if cursor || page_size
|
81
|
+
paginated = Server::Pagination.paginate(
|
82
|
+
items,
|
83
|
+
cursor: cursor,
|
84
|
+
page_size: page_size || 100,
|
85
|
+
cursor_ttl: cursor_ttl
|
86
|
+
)
|
87
|
+
|
88
|
+
PromptsData[prompts: paginated.items, next_cursor: paginated.next_cursor]
|
89
|
+
else
|
90
|
+
PromptsData[prompts: items]
|
91
|
+
end
|
79
92
|
end
|
80
93
|
|
81
|
-
def resources_data
|
82
|
-
|
94
|
+
def resources_data(cursor: nil, page_size: nil, cursor_ttl: nil)
|
95
|
+
items = @resources.map { |entry| entry.except(:klass) }
|
96
|
+
|
97
|
+
if cursor || page_size
|
98
|
+
paginated = Server::Pagination.paginate(
|
99
|
+
items,
|
100
|
+
cursor: cursor,
|
101
|
+
page_size: page_size || 100,
|
102
|
+
cursor_ttl: cursor_ttl
|
103
|
+
)
|
104
|
+
|
105
|
+
ResourcesData[resources: paginated.items, next_cursor: paginated.next_cursor]
|
106
|
+
else
|
107
|
+
ResourcesData[resources: items]
|
108
|
+
end
|
83
109
|
end
|
84
110
|
|
85
|
-
def resource_templates_data
|
86
|
-
|
111
|
+
def resource_templates_data(cursor: nil, page_size: nil, cursor_ttl: nil)
|
112
|
+
items = @resource_templates.map { |entry| entry.except(:klass, :completions) }
|
113
|
+
|
114
|
+
if cursor || page_size
|
115
|
+
paginated = Server::Pagination.paginate(
|
116
|
+
items,
|
117
|
+
cursor: cursor,
|
118
|
+
page_size: page_size || 100,
|
119
|
+
cursor_ttl: cursor_ttl
|
120
|
+
)
|
121
|
+
|
122
|
+
ResourceTemplatesData[resource_templates: paginated.items, next_cursor: paginated.next_cursor]
|
123
|
+
else
|
124
|
+
ResourceTemplatesData[resource_templates: items]
|
125
|
+
end
|
87
126
|
end
|
88
127
|
|
89
|
-
def tools_data
|
90
|
-
|
128
|
+
def tools_data(cursor: nil, page_size: nil, cursor_ttl: nil)
|
129
|
+
items = @tools.map { |entry| entry.except(:klass) }
|
130
|
+
|
131
|
+
if cursor || page_size
|
132
|
+
paginated = Server::Pagination.paginate(
|
133
|
+
items,
|
134
|
+
cursor: cursor,
|
135
|
+
page_size: page_size || 100,
|
136
|
+
cursor_ttl: cursor_ttl
|
137
|
+
)
|
138
|
+
|
139
|
+
ToolsData[tools: paginated.items, next_cursor: paginated.next_cursor]
|
140
|
+
else
|
141
|
+
ToolsData[tools: items]
|
142
|
+
end
|
91
143
|
end
|
92
144
|
|
93
145
|
private
|
94
146
|
|
95
|
-
PromptsData = Data.define(:prompts) do
|
147
|
+
PromptsData = Data.define(:prompts, :next_cursor) do
|
148
|
+
def initialize(prompts:, next_cursor: nil)
|
149
|
+
super
|
150
|
+
end
|
151
|
+
|
96
152
|
def serialized
|
97
|
-
{prompts:}
|
153
|
+
result = {prompts:}
|
154
|
+
result[:nextCursor] = next_cursor if next_cursor
|
155
|
+
result
|
98
156
|
end
|
99
157
|
end
|
100
158
|
|
101
|
-
ResourcesData = Data.define(:resources) do
|
159
|
+
ResourcesData = Data.define(:resources, :next_cursor) do
|
160
|
+
def initialize(resources:, next_cursor: nil)
|
161
|
+
super
|
162
|
+
end
|
163
|
+
|
102
164
|
def serialized
|
103
|
-
{resources:}
|
165
|
+
result = {resources:}
|
166
|
+
result[:nextCursor] = next_cursor if next_cursor
|
167
|
+
result
|
104
168
|
end
|
105
169
|
end
|
106
170
|
|
107
|
-
ResourceTemplatesData = Data.define(:resource_templates) do
|
171
|
+
ResourceTemplatesData = Data.define(:resource_templates, :next_cursor) do
|
172
|
+
def initialize(resource_templates:, next_cursor: nil)
|
173
|
+
super
|
174
|
+
end
|
175
|
+
|
108
176
|
def serialized
|
109
|
-
{resourceTemplates: resource_templates}
|
177
|
+
result = {resourceTemplates: resource_templates}
|
178
|
+
result[:nextCursor] = next_cursor if next_cursor
|
179
|
+
result
|
110
180
|
end
|
111
181
|
end
|
112
182
|
|
113
|
-
ToolsData = Data.define(:tools) do
|
183
|
+
ToolsData = Data.define(:tools, :next_cursor) do
|
184
|
+
def initialize(tools:, next_cursor: nil)
|
185
|
+
super
|
186
|
+
end
|
187
|
+
|
114
188
|
def serialized
|
115
|
-
{tools:}
|
189
|
+
result = {tools:}
|
190
|
+
result[:nextCursor] = next_cursor if next_cursor
|
191
|
+
result
|
116
192
|
end
|
117
193
|
end
|
118
194
|
|
@@ -1,12 +1,10 @@
|
|
1
1
|
module ModelContextProtocol
|
2
2
|
class Server::Resource
|
3
|
-
attr_reader :mime_type, :uri
|
3
|
+
attr_reader :mime_type, :uri
|
4
4
|
|
5
|
-
def initialize
|
5
|
+
def initialize
|
6
6
|
@mime_type = self.class.mime_type
|
7
7
|
@uri = self.class.uri
|
8
|
-
@context = context
|
9
|
-
@logger = logger
|
10
8
|
end
|
11
9
|
|
12
10
|
def call
|
@@ -15,59 +13,76 @@ module ModelContextProtocol
|
|
15
13
|
|
16
14
|
TextResponse = Data.define(:resource, :text) do
|
17
15
|
def serialized
|
18
|
-
|
16
|
+
content = {mimeType: resource.mime_type, text:, uri: resource.uri}
|
17
|
+
content[:title] = resource.class.title if resource.class.title
|
18
|
+
annotations = resource.class.annotations&.serialized
|
19
|
+
content[:annotations] = annotations if annotations
|
20
|
+
{contents: [content]}
|
19
21
|
end
|
20
22
|
end
|
21
23
|
private_constant :TextResponse
|
22
24
|
|
23
25
|
BinaryResponse = Data.define(:blob, :resource) do
|
24
26
|
def serialized
|
25
|
-
|
27
|
+
content = {blob:, mimeType: resource.mime_type, uri: resource.uri}
|
28
|
+
content[:title] = resource.class.title if resource.class.title
|
29
|
+
annotations = resource.class.annotations&.serialized
|
30
|
+
content[:annotations] = annotations if annotations
|
31
|
+
{contents: [content]}
|
26
32
|
end
|
27
33
|
end
|
28
34
|
private_constant :BinaryResponse
|
29
35
|
|
30
|
-
private def respond_with(
|
31
|
-
case [
|
32
|
-
in [
|
36
|
+
private def respond_with(**kwargs)
|
37
|
+
case [kwargs]
|
38
|
+
in [{text:}]
|
33
39
|
TextResponse[resource: self, text:]
|
34
|
-
in [
|
35
|
-
BinaryResponse[blob
|
40
|
+
in [{binary:}]
|
41
|
+
BinaryResponse[blob: binary, resource: self]
|
36
42
|
else
|
37
|
-
raise ModelContextProtocol::Server::ResponseArgumentsError, "Invalid arguments: #{
|
43
|
+
raise ModelContextProtocol::Server::ResponseArgumentsError, "Invalid arguments: #{options}"
|
38
44
|
end
|
39
45
|
end
|
40
46
|
|
41
47
|
class << self
|
42
|
-
attr_reader :name, :description, :mime_type, :uri
|
48
|
+
attr_reader :name, :description, :title, :mime_type, :uri, :annotations
|
43
49
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
50
|
+
def define(&block)
|
51
|
+
definition_dsl = DefinitionDSL.new
|
52
|
+
definition_dsl.instance_eval(&block)
|
47
53
|
|
48
|
-
@name =
|
49
|
-
@description =
|
50
|
-
@
|
51
|
-
@
|
54
|
+
@name = definition_dsl.name
|
55
|
+
@description = definition_dsl.description
|
56
|
+
@title = definition_dsl.title
|
57
|
+
@mime_type = definition_dsl.mime_type
|
58
|
+
@uri = definition_dsl.uri
|
59
|
+
@annotations = definition_dsl.defined_annotations
|
52
60
|
end
|
53
61
|
|
54
62
|
def inherited(subclass)
|
55
63
|
subclass.instance_variable_set(:@name, @name)
|
56
64
|
subclass.instance_variable_set(:@description, @description)
|
65
|
+
subclass.instance_variable_set(:@title, @title)
|
57
66
|
subclass.instance_variable_set(:@mime_type, @mime_type)
|
58
67
|
subclass.instance_variable_set(:@uri, @uri)
|
68
|
+
subclass.instance_variable_set(:@annotations, @annotations&.dup)
|
59
69
|
end
|
60
70
|
|
61
|
-
def call
|
62
|
-
new
|
71
|
+
def call
|
72
|
+
new.call
|
63
73
|
end
|
64
74
|
|
65
|
-
def
|
66
|
-
{name: @name, description: @description, mimeType: @mime_type, uri: @uri}
|
75
|
+
def definition
|
76
|
+
result = {name: @name, description: @description, mimeType: @mime_type, uri: @uri}
|
77
|
+
result[:title] = @title if @title
|
78
|
+
result[:annotations] = @annotations.serialized if @annotations
|
79
|
+
result
|
67
80
|
end
|
68
81
|
end
|
69
82
|
|
70
|
-
class
|
83
|
+
class DefinitionDSL
|
84
|
+
attr_reader :defined_annotations
|
85
|
+
|
71
86
|
def name(value = nil)
|
72
87
|
@name = value if value
|
73
88
|
@name
|
@@ -78,6 +93,11 @@ module ModelContextProtocol
|
|
78
93
|
@description
|
79
94
|
end
|
80
95
|
|
96
|
+
def title(value = nil)
|
97
|
+
@title = value if value
|
98
|
+
@title
|
99
|
+
end
|
100
|
+
|
81
101
|
def mime_type(value = nil)
|
82
102
|
@mime_type = value if value
|
83
103
|
@mime_type
|
@@ -87,6 +107,56 @@ module ModelContextProtocol
|
|
87
107
|
@uri = value if value
|
88
108
|
@uri
|
89
109
|
end
|
110
|
+
|
111
|
+
def annotations(&block)
|
112
|
+
@defined_annotations = AnnotationsDSL.new
|
113
|
+
@defined_annotations.instance_eval(&block)
|
114
|
+
@defined_annotations
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class AnnotationsDSL
|
119
|
+
VALID_AUDIENCE_VALUES = [:user, :assistant].freeze
|
120
|
+
|
121
|
+
def initialize
|
122
|
+
@audience = nil
|
123
|
+
@priority = nil
|
124
|
+
@last_modified = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def audience(value)
|
128
|
+
normalized_value = Array(value).map(&:to_sym)
|
129
|
+
invalid_values = normalized_value - VALID_AUDIENCE_VALUES
|
130
|
+
unless invalid_values.empty?
|
131
|
+
raise ArgumentError, "Invalid audience values: #{invalid_values.join(", ")}. Valid values are: #{VALID_AUDIENCE_VALUES.join(", ")}"
|
132
|
+
end
|
133
|
+
@audience = normalized_value.map(&:to_s)
|
134
|
+
end
|
135
|
+
|
136
|
+
def priority(value)
|
137
|
+
unless value.is_a?(Numeric) && value >= 0.0 && value <= 1.0
|
138
|
+
raise ArgumentError, "Priority must be a number between 0.0 and 1.0, got: #{value}"
|
139
|
+
end
|
140
|
+
@priority = value.to_f
|
141
|
+
end
|
142
|
+
|
143
|
+
def last_modified(value)
|
144
|
+
# Validate ISO 8601 format
|
145
|
+
begin
|
146
|
+
Time.iso8601(value)
|
147
|
+
rescue ArgumentError
|
148
|
+
raise ArgumentError, "lastModified must be in ISO 8601 format (e.g., '2025-01-12T15:00:58Z'), got: #{value}"
|
149
|
+
end
|
150
|
+
@last_modified = value
|
151
|
+
end
|
152
|
+
|
153
|
+
def serialized
|
154
|
+
result = {}
|
155
|
+
result[:audience] = @audience if @audience
|
156
|
+
result[:priority] = @priority if @priority
|
157
|
+
result[:lastModified] = @last_modified if @last_modified
|
158
|
+
result.empty? ? nil : result
|
159
|
+
end
|
90
160
|
end
|
91
161
|
end
|
92
162
|
end
|
@@ -3,15 +3,15 @@ module ModelContextProtocol
|
|
3
3
|
class << self
|
4
4
|
attr_reader :name, :description, :mime_type, :uri_template, :completions
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@name =
|
11
|
-
@description =
|
12
|
-
@mime_type =
|
13
|
-
@uri_template =
|
14
|
-
@completions =
|
6
|
+
def define(&block)
|
7
|
+
definition_dsl = DefinitionDSL.new
|
8
|
+
definition_dsl.instance_eval(&block)
|
9
|
+
|
10
|
+
@name = definition_dsl.name
|
11
|
+
@description = definition_dsl.description
|
12
|
+
@mime_type = definition_dsl.mime_type
|
13
|
+
@uri_template = definition_dsl.uri_template
|
14
|
+
@completions = definition_dsl.completions
|
15
15
|
end
|
16
16
|
|
17
17
|
def inherited(subclass)
|
@@ -32,7 +32,7 @@ module ModelContextProtocol
|
|
32
32
|
completion.call(param_name.to_s, value)
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def definition
|
36
36
|
{
|
37
37
|
name: @name,
|
38
38
|
description: @description,
|
@@ -43,7 +43,7 @@ module ModelContextProtocol
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
class
|
46
|
+
class DefinitionDSL
|
47
47
|
attr_reader :completions
|
48
48
|
|
49
49
|
def initialize
|
@@ -85,8 +85,21 @@ module ModelContextProtocol
|
|
85
85
|
@completions = {}
|
86
86
|
end
|
87
87
|
|
88
|
-
def completion(param_name,
|
89
|
-
@completions[param_name.to_s] =
|
88
|
+
def completion(param_name, completion_class_or_values)
|
89
|
+
@completions[param_name.to_s] = if completion_class_or_values.is_a?(Array)
|
90
|
+
create_array_completion(completion_class_or_values)
|
91
|
+
else
|
92
|
+
completion_class_or_values
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def create_array_completion(values)
|
99
|
+
ModelContextProtocol::Server::Completion.define do
|
100
|
+
filtered_values = values.grep(/#{argument_value}/)
|
101
|
+
respond_with values: filtered_values
|
102
|
+
end
|
90
103
|
end
|
91
104
|
end
|
92
105
|
end
|