Skip to content

Commit 6a95b4c

Browse files
Copilotstephentoub
andcommitted
Invert if block for common case, log send faults at debug level
Address review feedback: - Invert the WhenAny check so the common case (send completes first) is in the if branch. - Replace fire-and-forget ContinueWith with ObserveSendFaults that logs the exception at Debug level using the logging source generator. Uses ConfigureAwaitOptions.SuppressThrowing on .NET and ContinueWith on downlevel. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
1 parent fbf0638 commit 6a95b4c

1 file changed

Lines changed: 32 additions & 11 deletions

File tree

src/ModelContextProtocol.Core/McpSessionHandler.cs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -542,21 +542,39 @@ public async Task<JsonRpcResponse> SendRequestAsync(JsonRpcRequest request, Canc
542542
// that was already delivered via a different stream.
543543
using var sendCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
544544
Task sendTask = SendToRelatedTransportAsync(request, sendCts.Token);
545-
if (sendTask != await Task.WhenAny(sendTask, tcs.Task).ConfigureAwait(false))
545+
if (sendTask == await Task.WhenAny(sendTask, tcs.Task).ConfigureAwait(false))
546546
{
547-
// The response arrived via a concurrent channel before the transport send completed.
548-
// Cancel the still-running send and observe any exception to prevent unobserved task exceptions.
549-
sendCts.Cancel();
550-
_ = sendTask.ContinueWith(
551-
static (t, _) => _ = t.Exception,
552-
null,
553-
CancellationToken.None,
554-
TaskContinuationOptions.OnlyOnFaulted,
555-
TaskScheduler.Default);
547+
await sendTask.ConfigureAwait(false);
556548
}
557549
else
558550
{
559-
await sendTask.ConfigureAwait(false);
551+
// The response arrived via a concurrent channel before the transport send completed.
552+
// Cancel the still-running send and log any exception at debug level.
553+
sendCts.Cancel();
554+
_ = ObserveSendFaults(this, sendTask);
555+
556+
#if NET
557+
static async Task ObserveSendFaults(McpSessionHandler self, Task task)
558+
{
559+
await task.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
560+
if (task.IsFaulted)
561+
{
562+
self.LogTransportSendFaulted(self.EndpointName, task.Exception);
563+
}
564+
}
565+
#else
566+
static Task ObserveSendFaults(McpSessionHandler self, Task task) =>
567+
task.ContinueWith(
568+
static (t, s) =>
569+
{
570+
var handler = (McpSessionHandler)s!;
571+
handler.LogTransportSendFaulted(handler.EndpointName, t.Exception!);
572+
},
573+
self,
574+
CancellationToken.None,
575+
TaskContinuationOptions.OnlyOnFaulted,
576+
TaskScheduler.Default);
577+
#endif
560578
}
561579

562580
// Now that the request has been sent, register for cancellation. If we registered before,
@@ -1099,4 +1117,7 @@ private static McpProtocolException CreateRemoteProtocolException(JsonRpcError e
10991117

11001118
[LoggerMessage(Level = LogLevel.Trace, Message = "{EndpointName} session {SessionId} disposed with transport {TransportKind}")]
11011119
private partial void LogSessionDisposed(string endpointName, string sessionId, string transportKind);
1120+
1121+
[LoggerMessage(Level = LogLevel.Debug, Message = "{EndpointName} transport send faulted after response was already received.")]
1122+
private partial void LogTransportSendFaulted(string endpointName, Exception exception);
11021123
}

0 commit comments

Comments
 (0)