Skip to content

Commit b8fa0d8

Browse files
Revert TimeProvider NuGet dependency, use conditional compilation for test-only TimeProvider support
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
1 parent 0fd7769 commit b8fa0d8

5 files changed

Lines changed: 57 additions & 16 deletions

File tree

Directory.Packages.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
<!-- Product dependencies .NET Standard -->
2727
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
2828
<PackageVersion Include="Microsoft.Bcl.Memory" Version="$(System10Version)" />
29-
<PackageVersion Include="Microsoft.Bcl.TimeProvider" Version="$(System10Version)" />
3029
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="$(System10Version)" />
3130
<PackageVersion Include="System.IO.Pipelines" Version="$(System10Version)" />
3231
<PackageVersion Include="System.Text.Json" Version="$(System10Version)" />

src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
3434
<Compile Include="..\Common\CancellableStreamReader\**\*.cs" />
3535
<PackageReference Include="Microsoft.Bcl.Memory" />
36-
<PackageReference Include="Microsoft.Bcl.TimeProvider" />
3736
<PackageReference Include="System.Diagnostics.DiagnosticSource" />
3837
<PackageReference Include="System.Text.Json" />
3938
<PackageReference Include="System.Threading.Channels" />

src/ModelContextProtocol.Core/Server/InMemoryMcpTaskStore.cs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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)

tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
<IsTestProject>true</IsTestProject>
1111
<RootNamespace>ModelContextProtocol.Tests</RootNamespace>
1212
<!-- https://github.com/dotnet/sdk/issues/51060 -->
13-
<NoWarn>$(NoWarn);NU1903;NU1902</NoWarn>
13+
<NoWarn>$(NoWarn);NU1903;NU1902;CS0436</NoWarn>
14+
<!-- Define for conditional TimeProvider support in InMemoryMcpTaskStore -->
15+
<DefineConstants>$(DefineConstants);MCP_TEST_TIME_PROVIDER</DefineConstants>
1416
</PropertyGroup>
1517

1618
<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0'">
@@ -27,6 +29,10 @@
2729

2830
<ItemGroup>
2931
<Compile Include="..\Common\**\*.cs" />
32+
<!-- Link InMemoryMcpTaskStore.cs for testing with TimeProvider support -->
33+
<Compile Include="..\..\src\ModelContextProtocol.Core\Server\InMemoryMcpTaskStore.cs" Link="Server\InMemoryMcpTaskStore.cs" />
34+
<!-- Include dependencies for the linked InMemoryMcpTaskStore.cs -->
35+
<Compile Include="..\..\src\Common\Experimentals.cs" Link="Experimentals.cs" />
3036
</ItemGroup>
3137

3238
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">

tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,15 @@ public async Task ListTasksAsync_KeysetPaginationWorksWithIdenticalTimestamps()
10381038
{
10391039
// Arrange - Use a fake time provider to create tasks with identical timestamps
10401040
var fakeTime = new FakeTimeProvider(DateTimeOffset.UtcNow);
1041-
using var store = new InMemoryMcpTaskStore(pageSize: 5, timeProvider: fakeTime);
1041+
using var store = new InMemoryMcpTaskStore(
1042+
defaultTtl: null,
1043+
maxTtl: null,
1044+
pollInterval: null,
1045+
cleanupInterval: Timeout.InfiniteTimeSpan,
1046+
pageSize: 5,
1047+
maxTasks: null,
1048+
maxTasksPerSession: null,
1049+
timeProvider: fakeTime);
10421050

10431051
// Create 10 tasks - all with the EXACT same timestamp
10441052
var createdTasks = new List<McpTask>();

0 commit comments

Comments
 (0)