Class: SolidLoop::Mcp::StdioTransport
- Inherits:
-
Object
- Object
- SolidLoop::Mcp::StdioTransport
- Includes:
- Transport
- Defined in:
- lib/solid_loop/mcp/stdio_transport.rb
Overview
Child-process MCP server over stdin/stdout — restricted contract.
SolidLoop executes each tool call in an independent job (any worker, any
machine), so no process could own a long-lived child across rounds.
Therefore the lifecycle is spawn-per-call: spawn -> initialize
handshake -> request -> graceful shutdown (kill on timeout), all inside
one deliver. Stateless servers only: the session id is synthetic and
implies no server-side continuity; stateful stdio servers (a running
shell, a browser session, a REPL sandbox) are unsupported — their
in-memory state would be lost at every job boundary. This is a deliberate
consequence of the durable-async core, not a transport bug.
Options: command: (argv array, never a shell string), env:, cwd:, timeout: (per call, includes spawn+handshake). stderr is captured into the wire log. Spawn+handshake is typically 50-500 ms for Node/Python servers — prefer HTTP or a toolset for hot paths.
Defined Under Namespace
Classes: Diagnostics
Instance Method Summary collapse
- #deliver(payload, session_id: nil, context: nil) ⇒ Object
-
#initialize(command:, env: {}, cwd: nil, timeout: 30) ⇒ StdioTransport
constructor
A new instance of StdioTransport.
Constructor Details
#initialize(command:, env: {}, cwd: nil, timeout: 30) ⇒ StdioTransport
Returns a new instance of StdioTransport.
43 44 45 46 47 48 49 50 51 52 |
# File 'lib/solid_loop/mcp/stdio_transport.rb', line 43 def initialize(command:, env: {}, cwd: nil, timeout: 30) unless command.is_a?(Array) && command.any? && command.all?(String) raise ArgumentError, "command: must be a non-empty argv Array of Strings (never a shell string)" end @command = command @env = env.transform_keys(&:to_s).transform_values(&:to_s) @cwd = cwd @timeout = timeout end |
Instance Method Details
#deliver(payload, session_id: nil, context: nil) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/solid_loop/mcp/stdio_transport.rb', line 54 def deliver(payload, session_id: nil, context: nil) request = JSON.parse(JSON.generate(payload)) deadline = now + @timeout diagnostics = Diagnostics.new body = with_server(deadline, diagnostics) do |stdin, stdout| handshake(stdin, stdout, deadline, diagnostics) unless request["method"] == "initialize" (stdin, request, deadline, diagnostics) read_response(stdout, request["id"], deadline, diagnostics) end Result.new( body: body, session_id: nil, # stateless-only: no server-side continuity between calls raw_request: payload, raw_response: body, status: 200, stderr: diagnostics.to_s.strip.presence ) end |