Class: VectorMCP::Transport::Stdio

Inherits:
Object
  • Object
show all
Defined in:
lib/vector_mcp/transport/stdio.rb

Overview

Implements the Model Context Protocol transport over standard input/output (stdio). This transport reads JSON-RPC messages line-by-line from $stdin and writes responses/notifications line-by-line to $stdout.

It is suitable for inter-process communication on the same machine where a parent process spawns an MCP server and communicates with it via its stdio streams.

Constant Summary collapse

DEFAULT_REQUEST_TIMEOUT =

Timeout for waiting for a response to a server-initiated request (in seconds)

30

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(server) ⇒ Stdio

Initializes a new Stdio transport.

Parameters:



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/vector_mcp/transport/stdio.rb', line 30

def initialize(server)
  @server = server
  @logger = server.logger
  @input_mutex = Mutex.new
  @output_mutex = Mutex.new
  @running = false
  @input_thread = nil
  @shutdown_requested = false
  @outgoing_request_responses = {} # To store responses for server-initiated requests
  @outgoing_request_conditions = {} # ConditionVariables for server-initiated requests
  @mutex = Mutex.new # To synchronize access to shared response data
  @request_id_generator = Enumerator.new do |y|
    i = 0
    loop { y << "vecmcp_stdio_#{i += 1}_#{SecureRandom.hex(4)}" }
  end
end

Instance Attribute Details

#loggerLogger (readonly)

Returns The logger instance, shared with the server.

Returns:

  • (Logger)

    The logger instance, shared with the server.



22
23
24
# File 'lib/vector_mcp/transport/stdio.rb', line 22

def logger
  @logger
end

#serverVectorMCP::Server (readonly)

Returns The server instance this transport is bound to.

Returns:



20
21
22
# File 'lib/vector_mcp/transport/stdio.rb', line 20

def server
  @server
end

Instance Method Details

#runvoid

This method returns an undefined value.

Starts the stdio transport, listening for input and processing messages. This method will block until the input stream is closed or an interrupt is received.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/vector_mcp/transport/stdio.rb', line 51

def run
  session = create_session
  logger.info("Starting stdio transport")
  @running = true

  begin
    launch_input_thread(session)
    @input_thread.join
  rescue Interrupt
    logger.info("Interrupted. Shutting down...")
  ensure
    shutdown_transport
  end
end

#send_error(id, code, message, data = nil) ⇒ void

This method returns an undefined value.

Sends a JSON-RPC error response message.

Parameters:

  • id (String, Integer, nil)

    The ID of the request that caused the error.

  • code (Integer)

    The JSON-RPC error code.

  • message (String)

    A short description of the error.

  • data (Object, nil) (defaults to: nil)

    Additional error data (optional).



87
88
89
90
91
92
93
94
95
96
# File 'lib/vector_mcp/transport/stdio.rb', line 87

def send_error(id, code, message, data = nil)
  error_obj = { code: code, message: message }
  error_obj[:data] = data if data
  response = {
    jsonrpc: "2.0",
    id: id,
    error: error_obj
  }
  write_message(response)
end

#send_notification(method, params = nil) ⇒ void

This method returns an undefined value.

Sends a JSON-RPC notification message (a request without an ID).

Parameters:

  • method (String)

    The method name of the notification.

  • params (Hash, Array, nil) (defaults to: nil)

    The parameters for the notification (optional).



103
104
105
106
107
108
109
110
# File 'lib/vector_mcp/transport/stdio.rb', line 103

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

#send_request(method, params = nil, timeout: DEFAULT_REQUEST_TIMEOUT) ⇒ Object

Sends a server-initiated JSON-RPC request to the client and waits for a response. This is a blocking call.

Parameters:

  • method (String)

    The request method name.

  • params (Hash, Array, nil) (defaults to: nil)

    The request parameters.

  • timeout (Numeric) (defaults to: DEFAULT_REQUEST_TIMEOUT)

    How long to wait for a response, in seconds.

Returns:

  • (Object)

    The result part of the client's response.

Raises:



121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/vector_mcp/transport/stdio.rb', line 121

def send_request(method, params = nil, timeout: DEFAULT_REQUEST_TIMEOUT)
  raise ArgumentError, "Method cannot be blank" if method.to_s.strip.empty?

  request_id = @request_id_generator.next
  request_payload = { jsonrpc: "2.0", id: request_id, method: method }
  request_payload[:params] = params if params

  setup_request_tracking(request_id)
  logger.debug "[Stdio Transport] Sending request ID #{request_id}: #{method}"
  write_message(request_payload)

  response = wait_for_response(request_id, method, timeout)
  process_response(response, request_id, method)
end

#send_response(id, result) ⇒ void

This method returns an undefined value.

Sends a JSON-RPC response message for a given request ID.

Parameters:

  • id (String, Integer, nil)

    The ID of the request being responded to.

  • result (Object)

    The result data for the successful request.



71
72
73
74
75
76
77
78
# File 'lib/vector_mcp/transport/stdio.rb', line 71

def send_response(id, result)
  response = {
    jsonrpc: "2.0",
    id: id,
    result: result
  }
  write_message(response)
end

#shutdownvoid

This method returns an undefined value.

Initiates an immediate shutdown of the transport. Sets the running flag to false and attempts to kill the input reading thread.



140
141
142
143
144
# File 'lib/vector_mcp/transport/stdio.rb', line 140

def shutdown
  logger.info("Shutdown requested for stdio transport.")
  @running = false
  @input_thread&.kill if @input_thread&.alive?
end