Class: ModelContextProtocol::Server::StdioTransport

Inherits:
Object
  • Object
show all
Defined in:
lib/model_context_protocol/server/stdio_transport.rb,
lib/model_context_protocol/server/stdio_transport/request_store.rb

Defined Under Namespace

Classes: RequestStore

Constant Summary collapse

Response =
Data.define(:id, :result) do
  def serialized
    {jsonrpc: "2.0", id:, result:}
  end
end
ErrorResponse =
Data.define(:id, :error) do
  def serialized
    {jsonrpc: "2.0", id:, error:}
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(router:, configuration:) ⇒ StdioTransport

Returns a new instance of StdioTransport.



19
20
21
22
23
# File 'lib/model_context_protocol/server/stdio_transport.rb', line 19

def initialize(router:, configuration:)
  @router = router
  @configuration = configuration
  @request_store = RequestStore.new
end

Instance Attribute Details

#configurationObject (readonly)

Returns the value of attribute configuration.



17
18
19
# File 'lib/model_context_protocol/server/stdio_transport.rb', line 17

def configuration
  @configuration
end

#request_storeObject (readonly)

Returns the value of attribute request_store.



17
18
19
# File 'lib/model_context_protocol/server/stdio_transport.rb', line 17

def request_store
  @request_store
end

#routerObject (readonly)

Returns the value of attribute router.



17
18
19
# File 'lib/model_context_protocol/server/stdio_transport.rb', line 17

def router
  @router
end

Instance Method Details

#handleObject



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/model_context_protocol/server/stdio_transport.rb', line 25

def handle
  @configuration.logger.connect_transport(self)

  loop do
    line = receive_message
    break unless line

    begin
      message = JSON.parse(line.chomp)

      if message["method"] == "notifications/cancelled"
        handle_cancellation(message)
        next
      end

      next if message["method"]&.start_with?("notifications/")

      result = router.route(message, request_store: @request_store, transport: self)

      if result
        send_message(Response[id: message["id"], result: result.serialized])
      end
    rescue ModelContextProtocol::Server::ParameterValidationError => validation_error
      @configuration.logger.error("Validation error", error: validation_error.message)
      send_message(
        ErrorResponse[id: message["id"], error: {code: -32602, message: validation_error.message}]
      )
    rescue JSON::ParserError => parser_error
      @configuration.logger.error("Parser error", error: parser_error.message)
      send_message(
        ErrorResponse[id: "", error: {code: -32700, message: parser_error.message}]
      )
    rescue => error
      @configuration.logger.error("Internal error", error: error.message, backtrace: error.backtrace.first(5))
      send_message(
        ErrorResponse[id: message["id"], error: {code: -32603, message: error.message}]
      )
    end
  end
end

#send_notification(method, params) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/model_context_protocol/server/stdio_transport.rb', line 66

def send_notification(method, params)
  notification = {
    jsonrpc: "2.0",
    method: method,
    params: params
  }
  $stdout.puts(JSON.generate(notification))
  $stdout.flush
rescue IOError => e
  @configuration.logger.debug("Failed to send notification", error: e.message) if @configuration.logging_enabled?
end