Skip to content

Commit b9b4a23

Browse files
authored
Add a callback for receiving stderr output (#525)
* Add a callback for receiving stderr output * Add asserts to test
1 parent 4e2c1b9 commit b9b4a23

File tree

3 files changed

+35
-0
lines changed

3 files changed

+35
-0
lines changed

src/ModelContextProtocol.Core/Client/StdioClientTransport.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ public async Task<ITransport> ConnectAsync(CancellationToken cancellationToken =
146146
stderrRollingLog.Enqueue(data);
147147
}
148148

149+
_options.StandardErrorLines?.Invoke(data);
150+
149151
LogReadStderr(logger, endpointName, data);
150152
}
151153
};

src/ModelContextProtocol.Core/Client/StdioClientTransportOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,9 @@ public required string Command
6969
/// </para>
7070
/// </remarks>
7171
public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5);
72+
73+
/// <summary>
74+
/// Gets or sets a callback that is invoked for each line of stderr received from the server process.
75+
/// </summary>
76+
public Action<string>? StandardErrorLines { get; set; }
7277
}

tests/ModelContextProtocol.Tests/Transport/StdioClientTransportTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using ModelContextProtocol.Client;
22
using ModelContextProtocol.Tests.Utils;
33
using System.Runtime.InteropServices;
4+
using System.Text;
45

56
namespace ModelContextProtocol.Tests.Transport;
67

@@ -18,4 +19,31 @@ public async Task CreateAsync_ValidProcessInvalidServer_Throws()
1819
IOException e = await Assert.ThrowsAsync<IOException>(() => McpClientFactory.CreateAsync(transport, loggerFactory: LoggerFactory, cancellationToken: TestContext.Current.CancellationToken));
1920
Assert.Contains(id, e.ToString());
2021
}
22+
23+
[Fact]
24+
public async Task CreateAsync_ValidProcessInvalidServer_StdErrCallbackInvoked()
25+
{
26+
string id = Guid.NewGuid().ToString("N");
27+
28+
int count = 0;
29+
StringBuilder sb = new();
30+
Action<string> stdErrCallback = line =>
31+
{
32+
Assert.NotNull(line);
33+
lock (sb)
34+
{
35+
sb.AppendLine(line);
36+
count++;
37+
}
38+
};
39+
40+
StdioClientTransport transport = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
41+
new(new() { Command = "cmd", Arguments = ["/C", $"echo \"{id}\" >&2"], StandardErrorLines = stdErrCallback }, LoggerFactory) :
42+
new(new() { Command = "ls", Arguments = [id], StandardErrorLines = stdErrCallback }, LoggerFactory);
43+
44+
await Assert.ThrowsAsync<IOException>(() => McpClientFactory.CreateAsync(transport, loggerFactory: LoggerFactory, cancellationToken: TestContext.Current.CancellationToken));
45+
46+
Assert.InRange(count, 1, int.MaxValue);
47+
Assert.Contains(id, sb.ToString());
48+
}
2149
}

0 commit comments

Comments
 (0)