Class: VectorMCP::Transport::Stdio
- Inherits:
-
Object
- Object
- VectorMCP::Transport::Stdio
- 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
-
#logger ⇒ Logger
readonly
The logger instance, shared with the server.
-
#server ⇒ VectorMCP::Server
readonly
The server instance this transport is bound to.
Instance Method Summary collapse
-
#initialize(server) ⇒ Stdio
constructor
Initializes a new Stdio transport.
-
#run ⇒ void
Starts the stdio transport, listening for input and processing messages.
-
#send_error(id, code, message, data = nil) ⇒ void
Sends a JSON-RPC error response message.
-
#send_notification(method, params = nil) ⇒ void
Sends a JSON-RPC notification message (a request without an ID).
-
#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.
-
#send_response(id, result) ⇒ void
Sends a JSON-RPC response message for a given request ID.
-
#shutdown ⇒ void
Initiates an immediate shutdown of the transport.
Constructor Details
#initialize(server) ⇒ Stdio
Initializes a new Stdio transport.
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
#logger ⇒ Logger (readonly)
Returns The logger instance, shared with the server.
22 23 24 |
# File 'lib/vector_mcp/transport/stdio.rb', line 22 def logger @logger end |
#server ⇒ VectorMCP::Server (readonly)
Returns The server instance this transport is bound to.
20 21 22 |
# File 'lib/vector_mcp/transport/stdio.rb', line 20 def server @server end |
Instance Method Details
#run ⇒ void
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.
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/vector_mcp/transport/stdio.rb', line 87 def send_error(id, code, , data = nil) error_obj = { code: code, message: } error_obj[:data] = data if data response = { jsonrpc: "2.0", id: id, error: error_obj } (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).
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 (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.
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}" (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.
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 } (response) end |
#shutdown ⇒ void
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 |