Skip to content

Commit a0afc28

Browse files
authored
Remove TextReader/Writer parameterization from StdioServerTransport (#96)
It had been added only for tests.
1 parent 34a6f1c commit a0afc28

File tree

2 files changed

+50
-86
lines changed

2 files changed

+50
-86
lines changed

src/mcpdotnet/Protocol/Transport/StdioServerTransport.cs

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ public sealed class StdioServerTransport : TransportBase, IServerTransport
1919
{
2020
private readonly string _serverName;
2121
private readonly ILogger _logger;
22-
private readonly JsonSerializerOptions _jsonOptions;
22+
23+
private readonly JsonSerializerOptions _jsonOptions = JsonSerializerOptionsExtensions.DefaultOptions;
24+
private readonly TextReader _stdin = Console.In;
25+
private readonly TextWriter _stdout = Console.Out;
26+
2327
private Task? _readTask;
2428
private CancellationTokenSource? _shutdownCts;
25-
private readonly TextReader _input;
26-
private readonly TextWriter _output;
2729

2830
private string EndpointName => $"Server (stdio) ({_serverName})";
2931

@@ -59,62 +61,12 @@ public StdioServerTransport(McpServerOptions serverOptions, ILoggerFactory? logg
5961
/// </para>
6062
/// </remarks>
6163
public StdioServerTransport(string serverName, ILoggerFactory? loggerFactory = null)
62-
: this(serverName ?? throw new ArgumentNullException(nameof(serverName)), Console.In, Console.Out, loggerFactory)
63-
{
64-
}
65-
66-
/// <summary>
67-
/// Initializes a new instance of the <see cref="StdioServerTransport"/> class using the specified
68-
/// <paramref name="input"/> and <paramref name="output"/> streams.
69-
/// </summary>
70-
/// <param name="serverOptions">The server options.</param>
71-
/// <param name="input">The reader, from which all input is read.</param>
72-
/// <param name="output">The writer, to which all output is written.</param>
73-
/// <param name="loggerFactory">Optional logger factory used for logging employed by the transport.</param>
74-
/// <exception cref="ArgumentNullException"><paramref name="serverOptions"/> is <see langword="null"/> or contains a null name.</exception>
75-
/// <exception cref="ArgumentNullException"><paramref name="input"/> is <see langword="null"/>.</exception>
76-
/// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/>.</exception>
77-
/// <remarks>
78-
/// <para>
79-
/// By default, no logging is performed. If a <paramref name="loggerFactory"/> is supplied, it must not log
80-
/// to <see cref="Console.Out"/>, as that will interfere with the transport's output.
81-
/// </para>
82-
/// </remarks>
83-
public StdioServerTransport(McpServerOptions serverOptions, TextReader input, TextWriter output, ILoggerFactory? loggerFactory = null)
84-
: this(GetServerName(serverOptions), input, output, loggerFactory)
85-
{
86-
}
87-
88-
/// <summary>
89-
/// Initializes a new instance of the <see cref="StdioServerTransport"/> class using the specified
90-
/// <paramref name="input"/> and <paramref name="output"/> streams.
91-
/// </summary>
92-
/// <param name="serverName">The name of the server.</param>
93-
/// <param name="input">The reader, from which all input is read.</param>
94-
/// <param name="output">The writer, to which all output is written.</param>
95-
/// <param name="loggerFactory">Optional logger factory used for logging employed by the transport.</param>
96-
/// <exception cref="ArgumentNullException"><paramref name="serverName"/> is <see langword="null"/>.</exception>
97-
/// <exception cref="ArgumentNullException"><paramref name="input"/> is <see langword="null"/>.</exception>
98-
/// <exception cref="ArgumentNullException"><paramref name="output"/> is <see langword="null"/>.</exception>
99-
/// <remarks>
100-
/// <para>
101-
/// By default, no logging is performed. If a <paramref name="loggerFactory"/> is supplied, it must not log
102-
/// to <see cref="Console.Out"/>, as that will interfere with the transport's output.
103-
/// </para>
104-
/// </remarks>
105-
public StdioServerTransport(string serverName, TextReader input, TextWriter output, ILoggerFactory? loggerFactory = null)
10664
: base(loggerFactory)
10765
{
10866
Throw.IfNull(serverName);
109-
Throw.IfNull(input);
110-
Throw.IfNull(output);
111-
67+
11268
_serverName = serverName;
113-
_input = input;
114-
_output = output;
115-
11669
_logger = (ILogger?)loggerFactory?.CreateLogger<StdioClientTransport>() ?? NullLogger.Instance;
117-
_jsonOptions = JsonSerializerOptionsExtensions.DefaultOptions;
11870
}
11971

12072
/// <inheritdoc/>
@@ -149,8 +101,8 @@ public override async Task SendMessageAsync(IJsonRpcMessage message, Cancellatio
149101
var json = JsonSerializer.Serialize(message, _jsonOptions);
150102
_logger.TransportSendingMessage(EndpointName, id, json);
151103

152-
await _output.WriteLineAsync(json.AsMemory(), cancellationToken).ConfigureAwait(false);
153-
await _output.FlushAsync(cancellationToken).ConfigureAwait(false);
104+
await _stdout.WriteLineAsync(json.AsMemory(), cancellationToken).ConfigureAwait(false);
105+
await _stdout.FlushAsync(cancellationToken).ConfigureAwait(false);
154106

155107
_logger.TransportSentMessage(EndpointName, id);
156108
}
@@ -178,7 +130,7 @@ private async Task ReadMessagesAsync(CancellationToken cancellationToken)
178130
{
179131
_logger.TransportWaitingForMessage(EndpointName);
180132

181-
var reader = _input;
133+
var reader = _stdin;
182134
var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
183135
if (line == null)
184136
{

tests/mcpdotnet.Tests/Transport/StdioServerTransportTests.cs

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,6 @@ public void Constructor_Throws_For_Null_Options()
4747
Assert.Throws<ArgumentNullException>("serverName", () => new StdioServerTransport(new McpServerOptions() { ServerInfo = new() { Name = null!, Version = "" } }));
4848
}
4949

50-
[Fact]
51-
public void Constructor_Throws_For_Null_Streams()
52-
{
53-
Assert.Throws<ArgumentNullException>("input", () => new StdioServerTransport(_serverOptions, null!, Console.Out));
54-
Assert.Throws<ArgumentNullException>("input", () => new StdioServerTransport("name", null!, Console.Out));
55-
56-
Assert.Throws<ArgumentNullException>("output", () => new StdioServerTransport("name", Console.In, null!));
57-
Assert.Throws<ArgumentNullException>("output", () => new StdioServerTransport(_serverOptions, Console.In, null!));
58-
}
59-
6050
[Fact]
6151
public async Task StartListeningAsync_Should_Set_Connected_State()
6252
{
@@ -70,21 +60,33 @@ public async Task StartListeningAsync_Should_Set_Connected_State()
7060
[Fact]
7161
public async Task SendMessageAsync_Should_Send_Message()
7262
{
73-
var input = new StringReader("");
74-
var output = new StringWriter();
63+
TextReader oldIn = Console.In;
64+
TextWriter oldOut = Console.Out;
65+
try
66+
{
67+
var output = new StringWriter();
7568

76-
await using var transport = new StdioServerTransport(_serverOptions, input, output, NullLoggerFactory.Instance);
77-
await transport.StartListeningAsync();
69+
Console.SetIn(new StringReader(""));
70+
Console.SetOut(output);
7871

79-
var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) };
72+
await using var transport = new StdioServerTransport(_serverOptions, NullLoggerFactory.Instance);
73+
await transport.StartListeningAsync();
74+
75+
var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) };
8076

8177

82-
await transport.SendMessageAsync(message);
78+
await transport.SendMessageAsync(message);
8379

84-
var result = output.ToString()?.Trim();
85-
var expected = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions);
80+
var result = output.ToString()?.Trim();
81+
var expected = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions);
8682

87-
Assert.Equal(expected, result);
83+
Assert.Equal(expected, result);
84+
}
85+
finally
86+
{
87+
Console.SetOut(oldOut);
88+
Console.SetIn(oldIn);
89+
}
8890
}
8991

9092
[Fact]
@@ -113,19 +115,29 @@ public async Task ReadMessagesAsync_Should_Read_Messages()
113115
var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) };
114116
var json = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions);
115117

116-
var input = new StringReader(json);
117-
var output = new StringWriter();
118+
TextReader oldIn = Console.In;
119+
TextWriter oldOut = Console.Out;
120+
try
121+
{
122+
Console.SetIn(new StringReader(json));
123+
Console.SetOut(new StringWriter());
118124

119-
await using var transport = new StdioServerTransport(_serverOptions, input, output);
120-
await transport.StartListeningAsync();
125+
await using var transport = new StdioServerTransport(_serverOptions);
126+
await transport.StartListeningAsync();
121127

122-
var canRead = await transport.MessageReader.WaitToReadAsync();
128+
var canRead = await transport.MessageReader.WaitToReadAsync();
123129

124-
Assert.True(canRead, "Nothing to read here from transport message reader");
125-
Assert.True(transport.MessageReader.TryPeek(out var readMessage));
126-
Assert.NotNull(readMessage);
127-
Assert.IsType<JsonRpcRequest>(readMessage);
128-
Assert.Equal(44, ((JsonRpcRequest)readMessage).Id.AsNumber);
130+
Assert.True(canRead, "Nothing to read here from transport message reader");
131+
Assert.True(transport.MessageReader.TryPeek(out var readMessage));
132+
Assert.NotNull(readMessage);
133+
Assert.IsType<JsonRpcRequest>(readMessage);
134+
Assert.Equal(44, ((JsonRpcRequest)readMessage).Id.AsNumber);
135+
}
136+
finally
137+
{
138+
Console.SetOut(oldOut);
139+
Console.SetIn(oldIn);
140+
}
129141
}
130142

131143
[Fact]

0 commit comments

Comments
 (0)