- Renamed all directories: OpenHarbor.MCP.* → Svrnty.MCP.* - Updated all namespaces in 179 C# files - Renamed 20 .csproj files and 3 .sln files - Updated 193 documentation references - Updated 33 references in main CODEX codebase - Updated Codex.sln with new paths - Build verified: 0 errors Preparing for extraction to standalone repositories. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
118 lines
3.2 KiB
C#
118 lines
3.2 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Server transport implementation using stdio (standard input/output).
|
|
/// Launches a process and communicates via stdin/stdout.
|
|
/// </summary>
|
|
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<string>();
|
|
}
|
|
|
|
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<GatewayResponse> 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<GatewayResponse>(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();
|
|
}
|
|
}
|