Skip to content

Commit 47ed90d

Browse files
Copilotstephentoub
andcommitted
Fix race condition in McpSessionHandler.SendRequestAsync where foreground transport send blocks indefinitely when response arrives via background GET SSE stream
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
1 parent 5057f92 commit 47ed90d

1 file changed

Lines changed: 23 additions & 1 deletion

File tree

src/ModelContextProtocol.Core/McpSessionHandler.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,29 @@ public async Task<JsonRpcResponse> SendRequestAsync(JsonRpcRequest request, Canc
528528
LogSendingRequest(EndpointName, request.Method);
529529
}
530530

531-
await SendToRelatedTransportAsync(request, cancellationToken).ConfigureAwait(false);
531+
// Use a linked CTS so that we can cancel the transport send if the response TCS completes
532+
// from a concurrent channel (e.g. the background GET SSE stream in Streamable HTTP).
533+
// Without this, the foreground transport send could block indefinitely waiting for a response
534+
// that was already delivered via a different stream.
535+
using var sendCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
536+
537+
Task sendTask = SendToRelatedTransportAsync(request, sendCts.Token);
538+
if (!sendTask.IsCompleted)
539+
{
540+
_ = tcs.Task.ContinueWith(static (_, state) => ((CancellationTokenSource)state!).Cancel(), sendCts, sendCts.Token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
541+
}
542+
543+
try
544+
{
545+
await sendTask.ConfigureAwait(false);
546+
}
547+
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
548+
{
549+
}
550+
finally
551+
{
552+
sendCts.Cancel();
553+
}
532554

533555
// Now that the request has been sent, register for cancellation. If we registered before,
534556
// a cancellation request could arrive before the server knew about that request ID, in which

0 commit comments

Comments
 (0)