Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/ModelContextProtocol.Core/McpSessionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,26 @@ public async Task<JsonRpcResponse> SendRequestAsync(JsonRpcRequest request, Canc
LogSendingRequest(EndpointName, request.Method);
}

await SendToRelatedTransportAsync(request, cancellationToken).ConfigureAwait(false);
// Wait for either the transport send to complete or for the response to arrive via a
// concurrent channel (e.g. the background GET SSE stream in Streamable HTTP). Without
// this, the foreground transport send could block indefinitely waiting for a response
// that was already delivered via a different stream.
Task sendTask = SendToRelatedTransportAsync(request, cancellationToken);
if (sendTask != await Task.WhenAny(sendTask, tcs.Task).ConfigureAwait(false))
{
// The response arrived via a concurrent channel before the transport send completed.
// Observe any exception from the still-running send to prevent unobserved task exceptions.
_ = sendTask.ContinueWith(
static (t, _) => _ = t.Exception,
null,
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.Default);
}
else
{
await sendTask.ConfigureAwait(false);
}

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