mcp 0.1.0 → 0.3.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.
data/lib/mcp/server.rb CHANGED
@@ -20,24 +20,37 @@ module MCP
20
20
  end
21
21
  end
22
22
 
23
+ class MethodAlreadyDefinedError < StandardError
24
+ attr_reader :method_name
25
+
26
+ def initialize(method_name)
27
+ super("Method #{method_name} already defined")
28
+ @method_name = method_name
29
+ end
30
+ end
31
+
23
32
  include Instrumentation
24
33
 
25
- attr_writer :capabilities
26
- attr_accessor :name, :version, :tools, :prompts, :resources, :server_context, :configuration
34
+ attr_accessor :name, :title, :version, :instructions, :tools, :prompts, :resources, :server_context, :configuration, :capabilities, :transport
27
35
 
28
36
  def initialize(
29
37
  name: "model_context_protocol",
38
+ title: nil,
30
39
  version: DEFAULT_VERSION,
40
+ instructions: nil,
31
41
  tools: [],
32
42
  prompts: [],
33
43
  resources: [],
34
44
  resource_templates: [],
35
45
  server_context: nil,
36
46
  configuration: nil,
37
- capabilities: nil
47
+ capabilities: nil,
48
+ transport: nil
38
49
  )
39
50
  @name = name
51
+ @title = title
40
52
  @version = version
53
+ @instructions = instructions
41
54
  @tools = tools.to_h { |t| [t.name_value, t] }
42
55
  @prompts = prompts.to_h { |p| [p.name_value, p] }
43
56
  @resources = resources
@@ -46,6 +59,10 @@ module MCP
46
59
  @server_context = server_context
47
60
  @configuration = MCP.configuration.merge(configuration)
48
61
 
62
+ validate!
63
+
64
+ @capabilities = capabilities || default_capabilities
65
+
49
66
  @handlers = {
50
67
  Methods::RESOURCES_LIST => method(:list_resources),
51
68
  Methods::RESOURCES_READ => method(:read_resource_no_content),
@@ -56,6 +73,7 @@ module MCP
56
73
  Methods::PROMPTS_GET => method(:get_prompt),
57
74
  Methods::INITIALIZE => method(:init),
58
75
  Methods::PING => ->(_) { {} },
76
+ Methods::NOTIFICATIONS_INITIALIZED => ->(_) {},
59
77
 
60
78
  # No op handlers for currently unsupported methods
61
79
  Methods::RESOURCES_SUBSCRIBE => ->(_) {},
@@ -63,10 +81,7 @@ module MCP
63
81
  Methods::COMPLETION_COMPLETE => ->(_) {},
64
82
  Methods::LOGGING_SET_LEVEL => ->(_) {},
65
83
  }
66
- end
67
-
68
- def capabilities
69
- @capabilities ||= determine_capabilities
84
+ @transport = transport
70
85
  end
71
86
 
72
87
  def handle(request)
@@ -81,16 +96,50 @@ module MCP
81
96
  end
82
97
  end
83
98
 
84
- def define_tool(name: nil, description: nil, input_schema: nil, annotations: nil, &block)
85
- tool = Tool.define(name:, description:, input_schema:, annotations:, &block)
99
+ def define_tool(name: nil, title: nil, description: nil, input_schema: nil, annotations: nil, &block)
100
+ tool = Tool.define(name:, title:, description:, input_schema:, annotations:, &block)
86
101
  @tools[tool.name_value] = tool
102
+
103
+ validate!
87
104
  end
88
105
 
89
- def define_prompt(name: nil, description: nil, arguments: [], &block)
90
- prompt = Prompt.define(name:, description:, arguments:, &block)
106
+ def define_prompt(name: nil, title: nil, description: nil, arguments: [], &block)
107
+ prompt = Prompt.define(name:, title:, description:, arguments:, &block)
91
108
  @prompts[prompt.name_value] = prompt
92
109
  end
93
110
 
111
+ def define_custom_method(method_name:, &block)
112
+ if @handlers.key?(method_name)
113
+ raise MethodAlreadyDefinedError, method_name
114
+ end
115
+
116
+ @handlers[method_name] = block
117
+ end
118
+
119
+ def notify_tools_list_changed
120
+ return unless @transport
121
+
122
+ @transport.send_notification(Methods::NOTIFICATIONS_TOOLS_LIST_CHANGED)
123
+ rescue => e
124
+ report_exception(e, { notification: "tools_list_changed" })
125
+ end
126
+
127
+ def notify_prompts_list_changed
128
+ return unless @transport
129
+
130
+ @transport.send_notification(Methods::NOTIFICATIONS_PROMPTS_LIST_CHANGED)
131
+ rescue => e
132
+ report_exception(e, { notification: "prompts_list_changed" })
133
+ end
134
+
135
+ def notify_resources_list_changed
136
+ return unless @transport
137
+
138
+ @transport.send_notification(Methods::NOTIFICATIONS_RESOURCES_LIST_CHANGED)
139
+ rescue => e
140
+ report_exception(e, { notification: "resources_list_changed" })
141
+ end
142
+
94
143
  def resources_list_handler(&block)
95
144
  @handlers[Methods::RESOURCES_LIST] = block
96
145
  end
@@ -121,6 +170,25 @@ module MCP
121
170
 
122
171
  private
123
172
 
173
+ def validate!
174
+ if @configuration.protocol_version == "2024-11-05"
175
+ if @instructions
176
+ message = "`instructions` supported by protocol version 2025-03-26 or higher"
177
+ raise ArgumentError, message
178
+ end
179
+
180
+ error_tool_names = @tools.each_with_object([]) do |(tool_name, tool), error_tool_names|
181
+ if tool.annotations
182
+ error_tool_names << tool_name
183
+ end
184
+ end
185
+ unless error_tool_names.empty?
186
+ message = "Error occurred in #{error_tool_names.join(", ")}. `annotations` are supported by protocol version 2025-03-26 or higher"
187
+ raise ArgumentError, message
188
+ end
189
+ end
190
+ end
191
+
124
192
  def handle_request(request, method)
125
193
  handler = @handlers[method]
126
194
  unless handler
@@ -159,41 +227,36 @@ module MCP
159
227
  }
160
228
  end
161
229
 
162
- def determine_capabilities
163
- defines_prompts = @prompts.any? || @handlers[Methods::PROMPTS_LIST] != method(:list_prompts)
164
- defines_tools = @tools.any? || @handlers[Methods::TOOLS_LIST] != method(:list_tools)
165
- defines_resources = @resources.any? || @handlers[Methods::RESOURCES_LIST] != method(:list_resources)
166
- defines_resource_templates = @resource_templates.any? || @handlers[Methods::RESOURCES_TEMPLATES_LIST] != method(:list_resource_templates)
230
+ def default_capabilities
167
231
  {
168
- prompts: defines_prompts ? {} : nil,
169
- resources: defines_resources || defines_resource_templates ? {} : nil,
170
- tools: defines_tools ? {} : nil,
171
- }.compact
232
+ tools: { listChanged: true },
233
+ prompts: { listChanged: true },
234
+ resources: { listChanged: true },
235
+ }
172
236
  end
173
237
 
174
238
  def server_info
175
239
  @server_info ||= {
176
240
  name:,
241
+ title:,
177
242
  version:,
178
- }
243
+ }.compact
179
244
  end
180
245
 
181
246
  def init(request)
182
- add_instrumentation_data(method: Methods::INITIALIZE)
183
247
  {
184
248
  protocolVersion: configuration.protocol_version,
185
249
  capabilities: capabilities,
186
250
  serverInfo: server_info,
187
- }
251
+ instructions: instructions,
252
+ }.compact
188
253
  end
189
254
 
190
255
  def list_tools(request)
191
- add_instrumentation_data(method: Methods::TOOLS_LIST)
192
256
  @tools.map { |_, tool| tool.to_h }
193
257
  end
194
258
 
195
259
  def call_tool(request)
196
- add_instrumentation_data(method: Methods::TOOLS_CALL)
197
260
  tool_name = request[:name]
198
261
  tool = tools[tool_name]
199
262
  unless tool
@@ -201,7 +264,7 @@ module MCP
201
264
  raise RequestHandlerError.new("Tool not found #{tool_name}", request, error_type: :tool_not_found)
202
265
  end
203
266
 
204
- arguments = request[:arguments]
267
+ arguments = request[:arguments] || {}
205
268
  add_instrumentation_data(tool_name:)
206
269
 
207
270
  if tool.input_schema&.missing_required_arguments?(arguments)
@@ -213,26 +276,27 @@ module MCP
213
276
  )
214
277
  end
215
278
 
216
- begin
217
- call_params = tool_call_parameters(tool)
218
-
219
- if call_params.include?(:server_context)
220
- tool.call(**arguments.transform_keys(&:to_sym), server_context:).to_h
221
- else
222
- tool.call(**arguments.transform_keys(&:to_sym)).to_h
279
+ if configuration.validate_tool_call_arguments && tool.input_schema
280
+ begin
281
+ tool.input_schema.validate_arguments(arguments)
282
+ rescue Tool::InputSchema::ValidationError => e
283
+ add_instrumentation_data(error: :invalid_schema)
284
+ raise RequestHandlerError.new(e.message, request, error_type: :invalid_schema)
223
285
  end
286
+ end
287
+
288
+ begin
289
+ call_tool_with_args(tool, arguments)
224
290
  rescue => e
225
291
  raise RequestHandlerError.new("Internal error calling tool #{tool_name}", request, original_error: e)
226
292
  end
227
293
  end
228
294
 
229
295
  def list_prompts(request)
230
- add_instrumentation_data(method: Methods::PROMPTS_LIST)
231
296
  @prompts.map { |_, prompt| prompt.to_h }
232
297
  end
233
298
 
234
299
  def get_prompt(request)
235
- add_instrumentation_data(method: Methods::PROMPTS_GET)
236
300
  prompt_name = request[:name]
237
301
  prompt = @prompts[prompt_name]
238
302
  unless prompt
@@ -245,25 +309,20 @@ module MCP
245
309
  prompt_args = request[:arguments]
246
310
  prompt.validate_arguments!(prompt_args)
247
311
 
248
- prompt.template(prompt_args, server_context:).to_h
312
+ call_prompt_template_with_args(prompt, prompt_args)
249
313
  end
250
314
 
251
315
  def list_resources(request)
252
- add_instrumentation_data(method: Methods::RESOURCES_LIST)
253
-
254
316
  @resources.map(&:to_h)
255
317
  end
256
318
 
257
319
  # Server implementation should set `resources_read_handler` to override no-op default
258
320
  def read_resource_no_content(request)
259
- add_instrumentation_data(method: Methods::RESOURCES_READ)
260
321
  add_instrumentation_data(resource_uri: request[:uri])
261
322
  []
262
323
  end
263
324
 
264
325
  def list_resource_templates(request)
265
- add_instrumentation_data(method: Methods::RESOURCES_TEMPLATES_LIST)
266
-
267
326
  @resource_templates.map(&:to_h)
268
327
  end
269
328
 
@@ -277,22 +336,27 @@ module MCP
277
336
  end
278
337
  end
279
338
 
280
- def tool_call_parameters(tool)
281
- method_def = tool_call_method_def(tool)
282
- method_def.parameters.flatten
339
+ def accepts_server_context?(method_object)
340
+ parameters = method_object.parameters
341
+
342
+ parameters.any? { |type, name| type == :keyrest || name == :server_context }
283
343
  end
284
344
 
285
- def tool_call_method_def(tool)
286
- method = tool.method(:call)
345
+ def call_tool_with_args(tool, arguments)
346
+ args = arguments&.transform_keys(&:to_sym) || {}
287
347
 
288
- if defined?(T::Utils) && T::Utils.respond_to?(:signature_for_method)
289
- sorbet_typed_method_definition = T::Utils.signature_for_method(method)&.method
348
+ if accepts_server_context?(tool.method(:call))
349
+ tool.call(**args, server_context: server_context).to_h
350
+ else
351
+ tool.call(**args).to_h
352
+ end
353
+ end
290
354
 
291
- # Return the Sorbet typed method definition if it exists, otherwise fallback to original method
292
- # definition if Sorbet is defined but not used by this tool.
293
- sorbet_typed_method_definition || method
355
+ def call_prompt_template_with_args(prompt, args)
356
+ if accepts_server_context?(prompt.method(:template))
357
+ prompt.template(args, server_context: server_context).to_h
294
358
  else
295
- method
359
+ prompt.template(args).to_h
296
360
  end
297
361
  end
298
362
  end
@@ -3,9 +3,9 @@
3
3
  module MCP
4
4
  class Tool
5
5
  class Annotations
6
- attr_reader :title, :read_only_hint, :destructive_hint, :idempotent_hint, :open_world_hint
6
+ attr_reader :destructive_hint, :idempotent_hint, :open_world_hint, :read_only_hint, :title
7
7
 
8
- def initialize(title: nil, read_only_hint: nil, destructive_hint: nil, idempotent_hint: nil, open_world_hint: nil)
8
+ def initialize(destructive_hint: true, idempotent_hint: false, open_world_hint: true, read_only_hint: false, title: nil)
9
9
  @title = title
10
10
  @read_only_hint = read_only_hint
11
11
  @destructive_hint = destructive_hint
@@ -15,11 +15,11 @@ module MCP
15
15
 
16
16
  def to_h
17
17
  {
18
- title:,
19
- readOnlyHint: read_only_hint,
20
18
  destructiveHint: destructive_hint,
21
19
  idempotentHint: idempotent_hint,
22
20
  openWorldHint: open_world_hint,
21
+ readOnlyHint: read_only_hint,
22
+ title:,
23
23
  }.compact
24
24
  end
25
25
  end
@@ -1,17 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "json-schema"
4
+
3
5
  module MCP
4
6
  class Tool
5
7
  class InputSchema
8
+ class ValidationError < StandardError; end
9
+
6
10
  attr_reader :properties, :required
7
11
 
8
12
  def initialize(properties: {}, required: [])
9
13
  @properties = properties
10
14
  @required = required.map(&:to_sym)
15
+ validate_schema!
16
+ end
17
+
18
+ def ==(other)
19
+ other.is_a?(InputSchema) && properties == other.properties && required == other.required
11
20
  end
12
21
 
13
22
  def to_h
14
- { type: "object", properties:, required: }
23
+ { type: "object" }.tap do |hsh|
24
+ hsh[:properties] = properties if properties.any?
25
+ hsh[:required] = required if required.any?
26
+ end
15
27
  end
16
28
 
17
29
  def missing_required_arguments?(arguments)
@@ -21,6 +33,42 @@ module MCP
21
33
  def missing_required_arguments(arguments)
22
34
  (required - arguments.keys.map(&:to_sym))
23
35
  end
36
+
37
+ def validate_arguments(arguments)
38
+ errors = JSON::Validator.fully_validate(to_h, arguments)
39
+ if errors.any?
40
+ raise ValidationError, "Invalid arguments: #{errors.join(", ")}"
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def validate_schema!
47
+ check_for_refs!
48
+ schema = to_h
49
+ schema_reader = JSON::Schema::Reader.new(
50
+ accept_uri: false,
51
+ accept_file: ->(path) { path.to_s.start_with?(Gem.loaded_specs["json-schema"].full_gem_path) },
52
+ )
53
+ metaschema = JSON::Validator.validator_for_name("draft4").metaschema
54
+ errors = JSON::Validator.fully_validate(metaschema, schema, schema_reader: schema_reader)
55
+ if errors.any?
56
+ raise ArgumentError, "Invalid JSON Schema: #{errors.join(", ")}"
57
+ end
58
+ end
59
+
60
+ def check_for_refs!(obj = properties)
61
+ case obj
62
+ when Hash
63
+ if obj.key?("$ref") || obj.key?(:$ref)
64
+ raise ArgumentError, "Invalid JSON Schema: $ref is not allowed in tool input schemas"
65
+ end
66
+
67
+ obj.each_value { |value| check_for_refs!(value) }
68
+ when Array
69
+ obj.each { |item| check_for_refs!(item) }
70
+ end
71
+ end
24
72
  end
25
73
  end
26
74
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json-schema"
4
+
5
+ module MCP
6
+ class Tool
7
+ class OutputSchema
8
+ class ValidationError < StandardError; end
9
+
10
+ attr_reader :properties, :required
11
+
12
+ def initialize(properties: {}, required: [])
13
+ @properties = properties
14
+ @required = required.map(&:to_sym)
15
+ validate_schema!
16
+ end
17
+
18
+ def ==(other)
19
+ other.is_a?(OutputSchema) && properties == other.properties && required == other.required
20
+ end
21
+
22
+ def to_h
23
+ { type: "object" }.tap do |hsh|
24
+ hsh[:properties] = properties if properties.any?
25
+ hsh[:required] = required if required.any?
26
+ end
27
+ end
28
+
29
+ def validate_result(result)
30
+ errors = JSON::Validator.fully_validate(to_h, result)
31
+ if errors.any?
32
+ raise ValidationError, "Invalid result: #{errors.join(", ")}"
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def validate_schema!
39
+ check_for_refs!
40
+ schema = to_h
41
+ schema_reader = JSON::Schema::Reader.new(
42
+ accept_uri: false,
43
+ accept_file: ->(path) { path.to_s.start_with?(Gem.loaded_specs["json-schema"].full_gem_path) },
44
+ )
45
+ metaschema = JSON::Validator.validator_for_name("draft4").metaschema
46
+ errors = JSON::Validator.fully_validate(metaschema, schema, schema_reader: schema_reader)
47
+ if errors.any?
48
+ raise ArgumentError, "Invalid JSON Schema: #{errors.join(", ")}"
49
+ end
50
+ end
51
+
52
+ def check_for_refs!(obj = properties)
53
+ case obj
54
+ when Hash
55
+ if obj.key?("$ref") || obj.key?(:$ref)
56
+ raise ArgumentError, "Invalid JSON Schema: $ref is not allowed in tool output schemas"
57
+ end
58
+
59
+ obj.each_value { |value| check_for_refs!(value) }
60
+ when Array
61
+ obj.each { |item| check_for_refs!(item) }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -3,15 +3,26 @@
3
3
  module MCP
4
4
  class Tool
5
5
  class Response
6
- attr_reader :content, :is_error
6
+ NOT_GIVEN = Object.new.freeze
7
+
8
+ attr_reader :content
9
+
10
+ def initialize(content, deprecated_error = NOT_GIVEN, error: false)
11
+ if deprecated_error != NOT_GIVEN
12
+ warn("Passing `error` with the 2nd argument of `Response.new` is deprecated. Use keyword argument like `Response.new(content, error: error)` instead.", uplevel: 1)
13
+ error = deprecated_error
14
+ end
7
15
 
8
- def initialize(content, is_error = false)
9
16
  @content = content
10
- @is_error = is_error
17
+ @error = error
18
+ end
19
+
20
+ def error?
21
+ !!@error
11
22
  end
12
23
 
13
24
  def to_h
14
- { content:, isError: is_error }.compact
25
+ { content:, isError: error? }.compact
15
26
  end
16
27
  end
17
28
  end
data/lib/mcp/tool.rb CHANGED
@@ -5,29 +5,32 @@ module MCP
5
5
  class << self
6
6
  NOT_SET = Object.new
7
7
 
8
+ attr_reader :title_value
8
9
  attr_reader :description_value
9
- attr_reader :input_schema_value
10
10
  attr_reader :annotations_value
11
11
 
12
- def call(*args, server_context:)
12
+ def call(*args, server_context: nil)
13
13
  raise NotImplementedError, "Subclasses must implement call"
14
14
  end
15
15
 
16
16
  def to_h
17
- result = {
17
+ {
18
18
  name: name_value,
19
+ title: title_value,
19
20
  description: description_value,
20
21
  inputSchema: input_schema_value.to_h,
21
- }
22
- result[:annotations] = annotations_value.to_h if annotations_value
23
- result
22
+ outputSchema: @output_schema_value&.to_h,
23
+ annotations: annotations_value&.to_h,
24
+ }.compact
24
25
  end
25
26
 
26
27
  def inherited(subclass)
27
28
  super
28
29
  subclass.instance_variable_set(:@name_value, nil)
30
+ subclass.instance_variable_set(:@title_value, nil)
29
31
  subclass.instance_variable_set(:@description_value, nil)
30
32
  subclass.instance_variable_set(:@input_schema_value, nil)
33
+ subclass.instance_variable_set(:@output_schema_value, nil)
31
34
  subclass.instance_variable_set(:@annotations_value, nil)
32
35
  end
33
36
 
@@ -43,6 +46,20 @@ module MCP
43
46
  @name_value || StringUtils.handle_from_class_name(name)
44
47
  end
45
48
 
49
+ def input_schema_value
50
+ @input_schema_value || InputSchema.new
51
+ end
52
+
53
+ attr_reader :output_schema_value
54
+
55
+ def title(value = NOT_SET)
56
+ if value == NOT_SET
57
+ @title_value
58
+ else
59
+ @title_value = value
60
+ end
61
+ end
62
+
46
63
  def description(value = NOT_SET)
47
64
  if value == NOT_SET
48
65
  @description_value
@@ -63,6 +80,18 @@ module MCP
63
80
  end
64
81
  end
65
82
 
83
+ def output_schema(value = NOT_SET)
84
+ if value == NOT_SET
85
+ output_schema_value
86
+ elsif value.is_a?(Hash)
87
+ properties = value[:properties] || value["properties"] || {}
88
+ required = value[:required] || value["required"] || []
89
+ @output_schema_value = OutputSchema.new(properties:, required:)
90
+ elsif value.is_a?(OutputSchema)
91
+ @output_schema_value = value
92
+ end
93
+ end
94
+
66
95
  def annotations(hash = NOT_SET)
67
96
  if hash == NOT_SET
68
97
  @annotations_value
@@ -71,11 +100,13 @@ module MCP
71
100
  end
72
101
  end
73
102
 
74
- def define(name: nil, description: nil, input_schema: nil, annotations: nil, &block)
103
+ def define(name: nil, title: nil, description: nil, input_schema: nil, output_schema: nil, annotations: nil, &block)
75
104
  Class.new(self) do
76
105
  tool_name name
106
+ title title
77
107
  description description
78
108
  input_schema input_schema
109
+ output_schema output_schema
79
110
  self.annotations(annotations) if annotations
80
111
  define_singleton_method(:call, &block) if block
81
112
  end
data/lib/mcp/transport.rb CHANGED
@@ -2,32 +2,44 @@
2
2
 
3
3
  module MCP
4
4
  class Transport
5
+ # Initialize the transport with the server instance
5
6
  def initialize(server)
6
7
  @server = server
7
8
  end
8
9
 
10
+ # Send a response to the client
9
11
  def send_response(response)
10
12
  raise NotImplementedError, "Subclasses must implement send_response"
11
13
  end
12
14
 
15
+ # Open the transport connection
13
16
  def open
14
17
  raise NotImplementedError, "Subclasses must implement open"
15
18
  end
16
19
 
20
+ # Close the transport connection
17
21
  def close
18
22
  raise NotImplementedError, "Subclasses must implement close"
19
23
  end
20
24
 
21
- private
25
+ # Handle a JSON request
26
+ # Returns a response that should be sent back to the client
27
+ def handle_json_request(request)
28
+ response = @server.handle_json(request)
29
+ send_response(response) if response
30
+ end
22
31
 
32
+ # Handle an incoming request
33
+ # Returns a response that should be sent back to the client
23
34
  def handle_request(request)
24
35
  response = @server.handle(request)
25
36
  send_response(response) if response
26
37
  end
27
38
 
28
- def handle_json_request(request)
29
- response = @server.handle_json(request)
30
- send_response(response) if response
39
+ # Send a notification to the client
40
+ # Returns true if the notification was sent successfully
41
+ def send_notification(method, params = nil)
42
+ raise NotImplementedError, "Subclasses must implement send_notification"
31
43
  end
32
44
  end
33
45
  end
@@ -1,35 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../transport"
4
- require "json"
3
+ require_relative "../server/transports/stdio_transport"
4
+
5
+ warn <<~MESSAGE, uplevel: 3
6
+ Use `require "mcp/server/transports/stdio_transport"` instead of `require "mcp/transports/stdio"`.
7
+ Also use `MCP::Server::Transports::StdioTransport` instead of `MCP::Transports::StdioTransport`.
8
+ This API is deprecated and will be removed in a future release.
9
+ MESSAGE
5
10
 
6
11
  module MCP
7
12
  module Transports
8
- class StdioTransport < Transport
9
- def initialize(server)
10
- @server = server
11
- @open = false
12
- $stdin.set_encoding("UTF-8")
13
- $stdout.set_encoding("UTF-8")
14
- super
15
- end
16
-
17
- def open
18
- @open = true
19
- while @open && (line = $stdin.gets)
20
- handle_json_request(line.strip)
21
- end
22
- end
23
-
24
- def close
25
- @open = false
26
- end
27
-
28
- def send_response(message)
29
- json_message = message.is_a?(String) ? message : JSON.generate(message)
30
- $stdout.puts(json_message)
31
- $stdout.flush
32
- end
33
- end
13
+ StdioTransport = Server::Transports::StdioTransport
34
14
  end
35
15
  end