Class: VSM::MCP::JSONRPC::Stdio
- Inherits:
-
Object
- Object
- VSM::MCP::JSONRPC::Stdio
- Includes:
- MonitorMixin
- Defined in:
- lib/vsm/mcp/jsonrpc.rb
Overview
Minimal NDJSON (one JSON per line) JSON-RPC transport over IO. Note: MCP servers often speak LSP framing; we can add that later.
Instance Method Summary collapse
-
#initialize(r:, w:) ⇒ Stdio
constructor
A new instance of Stdio.
- #notify(method, params = {}) ⇒ Object
- #read ⇒ Object
- #request(method, params = {}) ⇒ Object
- #write(obj) ⇒ Object
Constructor Details
#initialize(r:, w:) ⇒ Stdio
Returns a new instance of Stdio.
13 14 15 16 17 18 |
# File 'lib/vsm/mcp/jsonrpc.rb', line 13 def initialize(r:, w:) @r = r @w = w @seq = 0 mon_initialize end |
Instance Method Details
#notify(method, params = {}) ⇒ Object
34 35 36 |
# File 'lib/vsm/mcp/jsonrpc.rb', line 34 def notify(method, params = {}) write({ jsonrpc: "2.0", method: method, params: params }) end |
#read ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/vsm/mcp/jsonrpc.rb', line 38 def read line = @r.gets return nil unless line # Handle LSP-style framing: "Content-Length: N" followed by blank line and JSON body. if line =~ /\AContent-Length:\s*(\d+)\s*\r?\n?\z/i length = Integer($1) # Consume optional additional headers until blank line while (hdr = @r.gets) break if hdr.strip.empty? end body = read_exact(length) $stderr.puts("[mcp-rpc] < #{body}") if ENV["VSM_MCP_DEBUG"] == "1" return JSON.parse(body) end # Otherwise assume NDJSON (one JSON object per line) $stderr.puts("[mcp-rpc] < #{line.strip}") if ENV["VSM_MCP_DEBUG"] == "1" JSON.parse(line) end |
#request(method, params = {}) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/vsm/mcp/jsonrpc.rb', line 20 def request(method, params = {}) id = next_id write({ jsonrpc: "2.0", id: id, method: method, params: params }) loop do msg = read next unless msg if msg["id"].to_s == id.to_s err = msg["error"] raise(err.is_a?(Hash) ? (err["message"] || err.inspect) : err.to_s) if err return msg["result"] end end end |
#write(obj) ⇒ Object
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/vsm/mcp/jsonrpc.rb', line 57 def write(obj) body = JSON.dump(obj) $stderr.puts("[mcp-rpc] > #{body}") if ENV["VSM_MCP_DEBUG"] == "1" synchronize do # Prefer NDJSON for broad compatibility; some servers require LSP. # If VSM_MCP_LSP=1, use Content-Length framing. if ENV["VSM_MCP_LSP"] == "1" @w.write("Content-Length: #{body.bytesize}\r\n\r\n") @w.write(body) @w.flush else @w.puts(body) @w.flush end end end |