Remove TextReader/Writer parameterization from StdioServerTransport (#96)
It had been added only for tests.
This commit is contained in:
parent
34a6f1c665
commit
a0afc28ad6
2 changed files with 50 additions and 86 deletions
|
|
@ -19,11 +19,13 @@ public sealed class StdioServerTransport : TransportBase, IServerTransport
|
||||||
{
|
{
|
||||||
private readonly string _serverName;
|
private readonly string _serverName;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly JsonSerializerOptions _jsonOptions;
|
|
||||||
|
private readonly JsonSerializerOptions _jsonOptions = JsonSerializerOptionsExtensions.DefaultOptions;
|
||||||
|
private readonly TextReader _stdin = Console.In;
|
||||||
|
private readonly TextWriter _stdout = Console.Out;
|
||||||
|
|
||||||
private Task? _readTask;
|
private Task? _readTask;
|
||||||
private CancellationTokenSource? _shutdownCts;
|
private CancellationTokenSource? _shutdownCts;
|
||||||
private readonly TextReader _input;
|
|
||||||
private readonly TextWriter _output;
|
|
||||||
|
|
||||||
private string EndpointName => $"Server (stdio) ({_serverName})";
|
private string EndpointName => $"Server (stdio) ({_serverName})";
|
||||||
|
|
||||||
|
|
@ -59,62 +61,12 @@ public sealed class StdioServerTransport : TransportBase, IServerTransport
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public StdioServerTransport(string serverName, ILoggerFactory? loggerFactory = null)
|
public StdioServerTransport(string serverName, ILoggerFactory? loggerFactory = null)
|
||||||
: this(serverName ?? throw new ArgumentNullException(nameof(serverName)), Console.In, Console.Out, loggerFactory)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="StdioServerTransport"/> class using the specified
|
|
||||||
/// <paramref name="input"/> and <paramref name="output"/> streams.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serverOptions">The server options.</param>
|
|
||||||
/// <param name="input">The reader, from which all input is read.</param>
|
|
||||||
/// <param name="output">The writer, to which all output is written.</param>
|
|
||||||
/// <param name="loggerFactory">Optional logger factory used for logging employed by the transport.</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="serverOptions"/> is <see langword="null"/> or contains a null name.</exception>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="input"/> is <see langword="null"/>.</exception>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/>.</exception>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// By default, no logging is performed. If a <paramref name="loggerFactory"/> is supplied, it must not log
|
|
||||||
/// to <see cref="Console.Out"/>, as that will interfere with the transport's output.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public StdioServerTransport(McpServerOptions serverOptions, TextReader input, TextWriter output, ILoggerFactory? loggerFactory = null)
|
|
||||||
: this(GetServerName(serverOptions), input, output, loggerFactory)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="StdioServerTransport"/> class using the specified
|
|
||||||
/// <paramref name="input"/> and <paramref name="output"/> streams.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serverName">The name of the server.</param>
|
|
||||||
/// <param name="input">The reader, from which all input is read.</param>
|
|
||||||
/// <param name="output">The writer, to which all output is written.</param>
|
|
||||||
/// <param name="loggerFactory">Optional logger factory used for logging employed by the transport.</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="serverName"/> is <see langword="null"/>.</exception>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="input"/> is <see langword="null"/>.</exception>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/>.</exception>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// By default, no logging is performed. If a <paramref name="loggerFactory"/> is supplied, it must not log
|
|
||||||
/// to <see cref="Console.Out"/>, as that will interfere with the transport's output.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public StdioServerTransport(string serverName, TextReader input, TextWriter output, ILoggerFactory? loggerFactory = null)
|
|
||||||
: base(loggerFactory)
|
: base(loggerFactory)
|
||||||
{
|
{
|
||||||
Throw.IfNull(serverName);
|
Throw.IfNull(serverName);
|
||||||
Throw.IfNull(input);
|
|
||||||
Throw.IfNull(output);
|
|
||||||
|
|
||||||
_serverName = serverName;
|
_serverName = serverName;
|
||||||
_input = input;
|
|
||||||
_output = output;
|
|
||||||
|
|
||||||
_logger = (ILogger?)loggerFactory?.CreateLogger<StdioClientTransport>() ?? NullLogger.Instance;
|
_logger = (ILogger?)loggerFactory?.CreateLogger<StdioClientTransport>() ?? NullLogger.Instance;
|
||||||
_jsonOptions = JsonSerializerOptionsExtensions.DefaultOptions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -149,8 +101,8 @@ public sealed class StdioServerTransport : TransportBase, IServerTransport
|
||||||
var json = JsonSerializer.Serialize(message, _jsonOptions);
|
var json = JsonSerializer.Serialize(message, _jsonOptions);
|
||||||
_logger.TransportSendingMessage(EndpointName, id, json);
|
_logger.TransportSendingMessage(EndpointName, id, json);
|
||||||
|
|
||||||
await _output.WriteLineAsync(json.AsMemory(), cancellationToken).ConfigureAwait(false);
|
await _stdout.WriteLineAsync(json.AsMemory(), cancellationToken).ConfigureAwait(false);
|
||||||
await _output.FlushAsync(cancellationToken).ConfigureAwait(false);
|
await _stdout.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.TransportSentMessage(EndpointName, id);
|
_logger.TransportSentMessage(EndpointName, id);
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +130,7 @@ public sealed class StdioServerTransport : TransportBase, IServerTransport
|
||||||
{
|
{
|
||||||
_logger.TransportWaitingForMessage(EndpointName);
|
_logger.TransportWaitingForMessage(EndpointName);
|
||||||
|
|
||||||
var reader = _input;
|
var reader = _stdin;
|
||||||
var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
|
var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
|
||||||
if (line == null)
|
if (line == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -47,16 +47,6 @@ public class StdioServerTransportTests
|
||||||
Assert.Throws<ArgumentNullException>("serverName", () => new StdioServerTransport(new McpServerOptions() { ServerInfo = new() { Name = null!, Version = "" } }));
|
Assert.Throws<ArgumentNullException>("serverName", () => new StdioServerTransport(new McpServerOptions() { ServerInfo = new() { Name = null!, Version = "" } }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Constructor_Throws_For_Null_Streams()
|
|
||||||
{
|
|
||||||
Assert.Throws<ArgumentNullException>("input", () => new StdioServerTransport(_serverOptions, null!, Console.Out));
|
|
||||||
Assert.Throws<ArgumentNullException>("input", () => new StdioServerTransport("name", null!, Console.Out));
|
|
||||||
|
|
||||||
Assert.Throws<ArgumentNullException>("output", () => new StdioServerTransport("name", Console.In, null!));
|
|
||||||
Assert.Throws<ArgumentNullException>("output", () => new StdioServerTransport(_serverOptions, Console.In, null!));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task StartListeningAsync_Should_Set_Connected_State()
|
public async Task StartListeningAsync_Should_Set_Connected_State()
|
||||||
{
|
{
|
||||||
|
|
@ -70,21 +60,33 @@ public class StdioServerTransportTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SendMessageAsync_Should_Send_Message()
|
public async Task SendMessageAsync_Should_Send_Message()
|
||||||
{
|
{
|
||||||
var input = new StringReader("");
|
TextReader oldIn = Console.In;
|
||||||
var output = new StringWriter();
|
TextWriter oldOut = Console.Out;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var output = new StringWriter();
|
||||||
|
|
||||||
await using var transport = new StdioServerTransport(_serverOptions, input, output, NullLoggerFactory.Instance);
|
Console.SetIn(new StringReader(""));
|
||||||
await transport.StartListeningAsync();
|
Console.SetOut(output);
|
||||||
|
|
||||||
var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) };
|
await using var transport = new StdioServerTransport(_serverOptions, NullLoggerFactory.Instance);
|
||||||
|
await transport.StartListeningAsync();
|
||||||
|
|
||||||
|
var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) };
|
||||||
|
|
||||||
|
|
||||||
await transport.SendMessageAsync(message);
|
await transport.SendMessageAsync(message);
|
||||||
|
|
||||||
var result = output.ToString()?.Trim();
|
var result = output.ToString()?.Trim();
|
||||||
var expected = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions);
|
var expected = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions);
|
||||||
|
|
||||||
Assert.Equal(expected, result);
|
Assert.Equal(expected, result);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Console.SetOut(oldOut);
|
||||||
|
Console.SetIn(oldIn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -113,19 +115,29 @@ public class StdioServerTransportTests
|
||||||
var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) };
|
var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) };
|
||||||
var json = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions);
|
var json = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions);
|
||||||
|
|
||||||
var input = new StringReader(json);
|
TextReader oldIn = Console.In;
|
||||||
var output = new StringWriter();
|
TextWriter oldOut = Console.Out;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.SetIn(new StringReader(json));
|
||||||
|
Console.SetOut(new StringWriter());
|
||||||
|
|
||||||
await using var transport = new StdioServerTransport(_serverOptions, input, output);
|
await using var transport = new StdioServerTransport(_serverOptions);
|
||||||
await transport.StartListeningAsync();
|
await transport.StartListeningAsync();
|
||||||
|
|
||||||
var canRead = await transport.MessageReader.WaitToReadAsync();
|
var canRead = await transport.MessageReader.WaitToReadAsync();
|
||||||
|
|
||||||
Assert.True(canRead, "Nothing to read here from transport message reader");
|
Assert.True(canRead, "Nothing to read here from transport message reader");
|
||||||
Assert.True(transport.MessageReader.TryPeek(out var readMessage));
|
Assert.True(transport.MessageReader.TryPeek(out var readMessage));
|
||||||
Assert.NotNull(readMessage);
|
Assert.NotNull(readMessage);
|
||||||
Assert.IsType<JsonRpcRequest>(readMessage);
|
Assert.IsType<JsonRpcRequest>(readMessage);
|
||||||
Assert.Equal(44, ((JsonRpcRequest)readMessage).Id.AsNumber);
|
Assert.Equal(44, ((JsonRpcRequest)readMessage).Id.AsNumber);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Console.SetOut(oldOut);
|
||||||
|
Console.SetIn(oldIn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue