Class: MCP::Server::Transports::StdioTransport

Inherits:
Transport
  • Object
show all
Defined in:
lib/mcp/server/transports/stdio_transport.rb

Constant Summary collapse

STATUS_INTERRUPTED =
Signal.list["INT"] + 128

Instance Method Summary collapse

Methods inherited from Transport

#handle_json_request, #handle_request

Constructor Details

#initialize(server) ⇒ StdioTransport

Returns a new instance of StdioTransport.



12
13
14
15
16
17
18
# File 'lib/mcp/server/transports/stdio_transport.rb', line 12

def initialize(server)
  super(server)
  @open = false
  @session = nil
  $stdin.set_encoding("UTF-8")
  $stdout.set_encoding("UTF-8")
end

Instance Method Details

#closeObject



33
34
35
# File 'lib/mcp/server/transports/stdio_transport.rb', line 33

def close
  @open = false
end

#openObject



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/mcp/server/transports/stdio_transport.rb', line 20

def open
  @open = true
  @session = ServerSession.new(server: @server, transport: self)
  while @open && (line = $stdin.gets)
    response = @session.handle_json(line.strip)
    send_response(response) if response
  end
rescue Interrupt
  warn("\nExiting...")

  exit(STATUS_INTERRUPTED)
end

#send_notification(method, params = nil) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/mcp/server/transports/stdio_transport.rb', line 43

def send_notification(method, params = nil)
  notification = {
    jsonrpc: "2.0",
    method: method,
  }
  notification[:params] = params if params

  send_response(notification)
  true
rescue => e
  MCP.configuration.exception_reporter.call(e, { error: "Failed to send notification" })
  false
end

#send_request(method, params = nil) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/mcp/server/transports/stdio_transport.rb', line 57

def send_request(method, params = nil)
  request_id = generate_request_id
  request = { jsonrpc: "2.0", id: request_id, method: method }
  request[:params] = params if params

  begin
    send_response(request)
  rescue => e
    MCP.configuration.exception_reporter.call(e, { error: "Failed to send request" })
    raise
  end

  while @open && (line = $stdin.gets)
    begin
      parsed = JSON.parse(line.strip, symbolize_names: true)
    rescue JSON::ParserError => e
      MCP.configuration.exception_reporter.call(e, { error: "Failed to parse response" })
      raise
    end

    if parsed[:id] == request_id && !parsed.key?(:method)
      if parsed[:error]
        raise StandardError, "Client returned an error for #{method} request (code: #{parsed[:error][:code]}): #{parsed[:error][:message]}"
      end

      return parsed[:result]
    else
      response = @session ? @session.handle(parsed) : @server.handle(parsed)
      send_response(response) if response
    end
  end

  raise "Transport closed while waiting for response to #{method} request."
end

#send_response(message) ⇒ Object



37
38
39
40
41
# File 'lib/mcp/server/transports/stdio_transport.rb', line 37

def send_response(message)
  json_message = message.is_a?(String) ? message : JSON.generate(message)
  $stdout.puts(json_message)
  $stdout.flush
end