Skip to content

Commit 11494e1

Browse files
committed
Server-side disconnect
1 parent 63fb022 commit 11494e1

File tree

2 files changed

+36
-0
lines changed

2 files changed

+36
-0
lines changed

src/ModelContextProtocol.Core/Server/RequestContext.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,21 @@ public McpServer Server
8181
/// including the method name, parameters, request ID, and associated transport and user information.
8282
/// </remarks>
8383
public JsonRpcRequest JsonRpcRequest { get; }
84+
85+
/// <summary>
86+
/// Ends the current response and enables polling for updates from the server.
87+
/// </summary>
88+
/// <param name="retryInterval">The interval at which the client should poll for updates.</param>
89+
/// <param name="cancellationToken">The cancellation token.</param>
90+
/// <returns>A <see cref="ValueTask"/> that completes when polling has been enabled.</returns>
91+
public async ValueTask EnablePollingAsync(TimeSpan retryInterval, CancellationToken cancellationToken = default)
92+
{
93+
if (JsonRpcRequest.Context?.RelatedTransport is not StreamableHttpPostTransport transport)
94+
{
95+
// Polling is only supported for Streamable HTTP POST transports.
96+
return;
97+
}
98+
99+
await transport.EnablePollingAsync(retryInterval, cancellationToken);
100+
}
84101
}

src/ModelContextProtocol.Core/Server/StreamableHttpPostTransport.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,25 @@ public async Task SendMessageAsync(JsonRpcMessage message, CancellationToken can
102102
}
103103
}
104104

105+
public async ValueTask EnablePollingAsync(TimeSpan retryInterval, CancellationToken cancellationToken)
106+
{
107+
var eventStreamWriter = await GetOrCreateEventStreamAsync(cancellationToken).ConfigureAwait(false);
108+
if (eventStreamWriter is null)
109+
{
110+
return;
111+
}
112+
113+
// Set the mode to 'Polling' so that the replay stream ends as soon as all available messages have been sent.
114+
// This prevents the client from immediately establishing another long-lived connection.
115+
await eventStreamWriter.SetModeAsync(SseEventStreamMode.Polling, cancellationToken).ConfigureAwait(false);
116+
117+
// Send the priming event with the new retry interval.
118+
await _sseWriter.SendPrimingEventAsync(retryInterval, eventStreamWriter, cancellationToken).ConfigureAwait(false);
119+
120+
// Dispose the writer to close it and force future writes to only apply to the SSE event store.
121+
await _sseWriter.DisposeAsync();
122+
}
123+
105124
public async ValueTask DisposeAsync()
106125
{
107126
await _sseWriter.DisposeAsync().ConfigureAwait(false);

0 commit comments

Comments
 (0)