using System.Diagnostics; using System.Text.Json; using Svrnty.MCP.Gateway.Core.Interfaces; using Svrnty.MCP.Gateway.Core.Models; namespace Svrnty.MCP.Gateway.Infrastructure.Transport; /// /// Server transport implementation using stdio (standard input/output). /// Launches a process and communicates via stdin/stdout. /// public class StdioServerTransport : IServerTransport { private readonly string _command; private readonly string[] _args; private Process? _process; private StreamWriter? _stdin; private StreamReader? _stdout; private bool _isConnected; public bool IsConnected => _isConnected; public StdioServerTransport(string command, string[] args) { _command = command ?? throw new ArgumentNullException(nameof(command)); _args = args ?? Array.Empty(); } public async Task ConnectAsync(CancellationToken cancellationToken = default) { if (_isConnected) { return; } _process = new Process { StartInfo = new ProcessStartInfo { FileName = _command, Arguments = string.Join(" ", _args), UseShellExecute = false, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; _process.Start(); _stdin = _process.StandardInput; _stdout = _process.StandardOutput; _isConnected = true; await Task.CompletedTask; } public async Task SendRequestAsync(GatewayRequest request, CancellationToken cancellationToken = default) { if (!_isConnected || _stdin == null || _stdout == null) { throw new InvalidOperationException("Transport is not connected"); } // Serialize request to JSON var jsonRequest = JsonSerializer.Serialize(request); await _stdin.WriteLineAsync(jsonRequest); await _stdin.FlushAsync(); // Read response from stdout var jsonResponse = await _stdout.ReadLineAsync(); if (string.IsNullOrEmpty(jsonResponse)) { return new GatewayResponse { Success = false, Error = "Empty response from server" }; } // Deserialize response var response = JsonSerializer.Deserialize(jsonResponse); return response ?? new GatewayResponse { Success = false, Error = "Failed to deserialize response" }; } public async Task DisconnectAsync(CancellationToken cancellationToken = default) { if (!_isConnected) { return; } _stdin?.Close(); _stdout?.Close(); if (_process != null && !_process.HasExited) { _process.Kill(); await _process.WaitForExitAsync(cancellationToken); } _process?.Dispose(); _process = null; _stdin = null; _stdout = null; _isConnected = false; } public void Dispose() { DisconnectAsync().GetAwaiter().GetResult(); } }