Skip to content

Commit 3ac44ac

Browse files
Copilotstephentoub
andcommitted
Fix session timeout by updating LastActivityTicks on every request
Update LastActivityTicks whenever a reference is acquired in the Started state, not just when reference count drops to 0. This ensures sessions with ongoing activity (POST requests) don't timeout even if a GET request is active. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
1 parent 8b354d4 commit 3ac44ac

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

src/ModelContextProtocol.AspNetCore/StreamableHttpSession.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ public async ValueTask<IAsyncDisposable> AcquireReferenceAsync(CancellationToken
5858
{
5959
sessionManager.DecrementIdleSessionCount();
6060
}
61+
// Update LastActivityTicks whenever a request is made to keep the session alive
62+
LastActivityTicks = sessionManager.TimeProvider.GetTimestamp();
6163
break;
6264
case SessionState.Disposed:
6365
throw new ObjectDisposedException(nameof(StreamableHttpSession));

tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,33 @@ public async Task IdleSessionsPastMaxIdleSessionCount_ArePruned_LongestIdleFirst
521521
Assert.StartsWith("MaxIdleSessionCount of 2 exceeded. Closing idle session", idleLimitLogMessage.Message);
522522
}
523523

524+
[Fact]
525+
public async Task ActiveSession_WithPeriodicRequests_DoesNotTimeout()
526+
{
527+
var fakeTimeProvider = new FakeTimeProvider();
528+
Builder.Services.AddMcpServer().WithHttpTransport(options =>
529+
{
530+
options.IdleTimeout = TimeSpan.FromHours(2);
531+
options.TimeProvider = fakeTimeProvider;
532+
});
533+
534+
await StartAsync();
535+
await CallInitializeAndValidateAsync();
536+
537+
// Simulate multiple POST requests over a period longer than IdleTimeout
538+
// Each request should update LastActivityTicks, preventing timeout
539+
for (int i = 0; i < 5; i++)
540+
{
541+
// Advance time by 1 hour between requests
542+
fakeTimeProvider.Advance(TimeSpan.FromHours(1));
543+
await CallEchoAndValidateAsync();
544+
}
545+
546+
// Total time elapsed: 5 hours (> 2 hour IdleTimeout)
547+
// But session should still be alive because of periodic activity
548+
await CallEchoAndValidateAsync();
549+
}
550+
524551
[Fact]
525552
public async Task McpServer_UsedOutOfScope_CanSendNotifications()
526553
{

0 commit comments

Comments
 (0)