@@ -35,7 +35,9 @@ public sealed class InMemoryMcpTaskStore : IMcpTaskStore, IDisposable
3535 private readonly int _pageSize ;
3636 private readonly int ? _maxTasks ;
3737 private readonly int ? _maxTasksPerSession ;
38+ #if MCP_TEST_TIME_PROVIDER
3839 private readonly TimeProvider _timeProvider ;
40+ #endif
3941
4042 /// <summary>
4143 /// Initializes a new instance of the <see cref="InMemoryMcpTaskStore"/> class.
@@ -66,19 +68,14 @@ public sealed class InMemoryMcpTaskStore : IMcpTaskStore, IDisposable
6668 /// Maximum number of tasks allowed per session. Null means unlimited.
6769 /// When the limit is reached for a session, <see cref="CreateTaskAsync"/> will throw <see cref="InvalidOperationException"/>.
6870 /// </param>
69- /// <param name="timeProvider">
70- /// Time provider for getting the current time. Defaults to <see cref="TimeProvider.System"/>.
71- /// This parameter is primarily useful for testing scenarios where you need to control time.
72- /// </param>
7371 public InMemoryMcpTaskStore (
7472 TimeSpan ? defaultTtl = null ,
7573 TimeSpan ? maxTtl = null ,
7674 TimeSpan ? pollInterval = null ,
7775 TimeSpan ? cleanupInterval = null ,
7876 int pageSize = 100 ,
7977 int ? maxTasks = null ,
80- int ? maxTasksPerSession = null ,
81- TimeProvider ? timeProvider = null )
78+ int ? maxTasksPerSession = null )
8279 {
8380 if ( defaultTtl . HasValue && maxTtl . HasValue && defaultTtl . Value > maxTtl . Value )
8481 {
@@ -126,7 +123,9 @@ public InMemoryMcpTaskStore(
126123 _pageSize = pageSize ;
127124 _maxTasks = maxTasks ;
128125 _maxTasksPerSession = maxTasksPerSession ;
129- _timeProvider = timeProvider ?? TimeProvider . System ;
126+ #if MCP_TEST_TIME_PROVIDER
127+ _timeProvider = TimeProvider . System ;
128+ #endif
130129
131130 cleanupInterval ??= TimeSpan . FromMinutes ( 1 ) ;
132131 if ( cleanupInterval . Value != Timeout . InfiniteTimeSpan )
@@ -135,6 +134,26 @@ public InMemoryMcpTaskStore(
135134 }
136135 }
137136
137+ #if MCP_TEST_TIME_PROVIDER
138+ /// <summary>
139+ /// Initializes a new instance of the <see cref="InMemoryMcpTaskStore"/> class with a custom time provider.
140+ /// This constructor is only available for testing purposes.
141+ /// </summary>
142+ internal InMemoryMcpTaskStore (
143+ TimeSpan ? defaultTtl ,
144+ TimeSpan ? maxTtl ,
145+ TimeSpan ? pollInterval ,
146+ TimeSpan ? cleanupInterval ,
147+ int pageSize ,
148+ int ? maxTasks ,
149+ int ? maxTasksPerSession ,
150+ TimeProvider timeProvider )
151+ : this ( defaultTtl , maxTtl , pollInterval , cleanupInterval , pageSize , maxTasks , maxTasksPerSession )
152+ {
153+ _timeProvider = timeProvider ?? TimeProvider . System ;
154+ }
155+ #endif
156+
138157 /// <inheritdoc/>
139158 public Task < McpTask > CreateTaskAsync (
140159 McpTaskMetadata taskParams ,
@@ -162,7 +181,7 @@ public Task<McpTask> CreateTaskAsync(
162181 }
163182
164183 var taskId = GenerateTaskId ( ) ;
165- var now = _timeProvider . GetUtcNow ( ) ;
184+ var now = GetUtcNow ( ) ;
166185
167186 // Determine TTL: use requested, fall back to default, respect max limit
168187 var ttl = taskParams . TimeToLive ?? _defaultTtl ;
@@ -249,7 +268,7 @@ public Task<McpTask> StoreTaskResultAsync(
249268 var updatedEntry = new TaskEntry ( entry )
250269 {
251270 Status = status ,
252- LastUpdatedAt = _timeProvider . GetUtcNow ( ) ,
271+ LastUpdatedAt = GetUtcNow ( ) ,
253272 StoredResult = result
254273 } ;
255274
@@ -310,7 +329,7 @@ public Task<McpTask> UpdateTaskStatusAsync(
310329 {
311330 Status = status ,
312331 StatusMessage = statusMessage ,
313- LastUpdatedAt = _timeProvider . GetUtcNow ( ) ,
332+ LastUpdatedAt = GetUtcNow ( ) ,
314333 } ;
315334
316335 if ( _tasks . TryUpdate ( taskId , updatedEntry , entry ) )
@@ -405,7 +424,7 @@ public Task<McpTask> CancelTaskAsync(string taskId, string? sessionId = null, Ca
405424 var updatedEntry = new TaskEntry ( entry )
406425 {
407426 Status = McpTaskStatus . Cancelled ,
408- LastUpdatedAt = _timeProvider . GetUtcNow ( ) ,
427+ LastUpdatedAt = GetUtcNow ( ) ,
409428 } ;
410429
411430 if ( _tasks . TryUpdate ( taskId , updatedEntry , entry ) )
@@ -430,15 +449,25 @@ public void Dispose()
430449 private static bool IsTerminalStatus ( McpTaskStatus status ) =>
431450 status is McpTaskStatus . Completed or McpTaskStatus . Failed or McpTaskStatus . Cancelled ;
432451
452+ #if MCP_TEST_TIME_PROVIDER
453+ private DateTimeOffset GetUtcNow ( ) => _timeProvider . GetUtcNow ( ) ;
454+ #else
455+ private static DateTimeOffset GetUtcNow ( ) => DateTimeOffset . UtcNow ;
456+ #endif
457+
458+ #if MCP_TEST_TIME_PROVIDER
433459 private bool IsExpired ( TaskEntry entry )
460+ #else
461+ private static bool IsExpired ( TaskEntry entry )
462+ #endif
434463 {
435464 if ( entry . TimeToLive == null )
436465 {
437466 return false ; // Unlimited lifetime
438467 }
439468
440469 var expirationTime = entry . CreatedAt + entry . TimeToLive . Value ;
441- return _timeProvider . GetUtcNow ( ) >= expirationTime ;
470+ return GetUtcNow ( ) >= expirationTime ;
442471 }
443472
444473 private void CleanupExpiredTasks ( object ? state )
0 commit comments