perf(server): skip IdleTrackingBackgroundService timer in stateless mode (#1531)

This commit is contained in:
Mukunda Rao Katta 2026-06-05 18:24:34 -05:00 • committed by GitHub
commit a4157f310f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using ModelContextProtocol.AspNetCore.Tests.Utils;
using ModelContextProtocol.Protocol;
@ -184,6 +185,55 @@ public class HttpMcpServerBuilderExtensionsTests(ITestOutputHelper testOutputHel
Assert.Null(options.SessionMigrationHandler);
}
[Fact]
public async Task IdleTrackingBackgroundService_DoesNotStartTimer_WhenStateless()
{
Builder.Services
.AddMcpServer()
.WithHttpTransport(options => options.Stateless = true);
using var app = Builder.Build();
var idleTrackingService = GetIdleTrackingService(app.Services);
Assert.NotNull(idleTrackingService);
await idleTrackingService.StartAsync(TestContext.Current.CancellationToken);
// BackgroundService.ExecuteTask is only set when ExecuteAsync has been kicked off via base.StartAsync.
// In stateless mode we early-return, so ExecuteTask should remain null.
Assert.Null(idleTrackingService.ExecuteTask);
await idleTrackingService.StopAsync(TestContext.Current.CancellationToken);
}
[Fact]
public async Task IdleTrackingBackgroundService_StartsTimer_WhenStateful()
{
Builder.Services
.AddMcpServer()
.WithHttpTransport();
using var app = Builder.Build();
var idleTrackingService = GetIdleTrackingService(app.Services);
Assert.NotNull(idleTrackingService);
await idleTrackingService.StartAsync(TestContext.Current.CancellationToken);
// In the default (stateful) mode the timer loop must start, so ExecuteTask should be set.
Assert.NotNull(idleTrackingService.ExecuteTask);
await idleTrackingService.StopAsync(TestContext.Current.CancellationToken);
}
private static BackgroundService? GetIdleTrackingService(IServiceProvider services)
{
// IdleTrackingBackgroundService is internal, so look it up by type name from the registered IHostedService instances.
return services.GetServices<IHostedService>()
.OfType<BackgroundService>()
.FirstOrDefault(s => s.GetType().Name == "IdleTrackingBackgroundService");
}
private sealed class StubSessionMigrationHandler : ISessionMigrationHandler
{
public ValueTask<InitializeRequestParams?> AllowSessionMigrationAsync(