From 35df5a1fd4ec0c3294ce3cfec775ab254971e316 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 18:53:42 +0000 Subject: [PATCH 1/4] Initial plan From 7d83b084e57f3488d5d27ecd5b91474c0e393b07 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 19:13:02 +0000 Subject: [PATCH 2/4] Fix race condition in StdioClientSessionTransport stderr handling Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Client/StdioClientSessionTransport.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs b/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs index a9c228d43..379176ddb 100644 --- a/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs +++ b/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs @@ -74,6 +74,10 @@ protected override async ValueTask CleanupAsync(Exception? error = null, Cancell // The process has exited, but we still need to ensure stderr has been flushed. #if NET await _process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); + // WaitForExitAsync only waits for the process to exit, not for async stderr/stdout to complete. + // We need to call the parameterless WaitForExit to ensure async I/O is fully flushed. + // See: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit + _process.WaitForExit(); #else _process.WaitForExit(); #endif From 4b8a9479c1310119fc1a911104482d30212adcab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 19:48:33 +0000 Subject: [PATCH 3/4] Simplify WaitForExit logic as suggested in code review Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Client/StdioClientSessionTransport.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs b/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs index 379176ddb..b53bc1f24 100644 --- a/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs +++ b/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs @@ -74,13 +74,8 @@ protected override async ValueTask CleanupAsync(Exception? error = null, Cancell // The process has exited, but we still need to ensure stderr has been flushed. #if NET await _process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); - // WaitForExitAsync only waits for the process to exit, not for async stderr/stdout to complete. - // We need to call the parameterless WaitForExit to ensure async I/O is fully flushed. - // See: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit - _process.WaitForExit(); -#else - _process.WaitForExit(); #endif + _process.WaitForExit(); } catch { } From 688a6023fee60af9e6faaf0dee2118e55d96a373 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:01:31 +0000 Subject: [PATCH 4/4] Skip flaky test on Mono and revert incorrect fix Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Client/StdioClientSessionTransport.cs | 3 ++- .../Transport/StdioClientTransportTests.cs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs b/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs index b53bc1f24..a9c228d43 100644 --- a/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs +++ b/src/ModelContextProtocol.Core/Client/StdioClientSessionTransport.cs @@ -74,8 +74,9 @@ protected override async ValueTask CleanupAsync(Exception? error = null, Cancell // The process has exited, but we still need to ensure stderr has been flushed. #if NET await _process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); -#endif +#else _process.WaitForExit(); +#endif } catch { } diff --git a/tests/ModelContextProtocol.Tests/Transport/StdioClientTransportTests.cs b/tests/ModelContextProtocol.Tests/Transport/StdioClientTransportTests.cs index 5394ba30e..7f9cd61a5 100644 --- a/tests/ModelContextProtocol.Tests/Transport/StdioClientTransportTests.cs +++ b/tests/ModelContextProtocol.Tests/Transport/StdioClientTransportTests.cs @@ -22,8 +22,7 @@ public async Task CreateAsync_ValidProcessInvalidServer_Throws() await Assert.ThrowsAsync(() => McpClient.CreateAsync(transport, loggerFactory: LoggerFactory, cancellationToken: TestContext.Current.CancellationToken)); } - // [Fact(Skip = "Platform not supported by this test.", SkipUnless = nameof(IsStdErrCallbackSupported))] - [Fact] + [Fact(Skip = "Platform not supported by this test.", SkipUnless = nameof(IsStdErrCallbackSupported))] public async Task CreateAsync_ValidProcessInvalidServer_StdErrCallbackInvoked() { string id = Guid.NewGuid().ToString("N");