Skip to content
Merged
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0e085be
Initial plan
Copilot Jan 30, 2026
f32e1e1
Fix sporadic test failure by using monotonically increasing task IDs
Copilot Jan 30, 2026
b699d69
Fix keyset pagination by sorting before applying cursor filter
Copilot Jan 30, 2026
fba8ccf
Refactor keyset pagination code styling per review feedback
Copilot Jan 30, 2026
0fd7769
Add TimeProvider support and test for identical timestamp handling
Copilot Jan 30, 2026
b8fa0d8
Revert TimeProvider NuGet dependency, use conditional compilation for…
Copilot Jan 30, 2026
1213ce8
Add comment explaining CS0436 suppression for linked InMemoryMcpTaskS…
Copilot Jan 30, 2026
a80c8b4
Use conditional namespace to avoid CS0436 type conflicts
Copilot Jan 30, 2026
32dbb40
Use monotonic UUID v7 for task IDs with polyfill for older targets
Copilot Jan 30, 2026
fdd7e85
Move UUID v7 polyfill to Common/Polyfills/System/GuidPolyfills.cs
Copilot Jan 30, 2026
4053b29
Revert ListTasksAsync changes, use monotonic GUID with TimeProvider s…
Copilot Jan 30, 2026
a637ea2
Move UUID v7 generation logic back to GuidPolyfills class
Copilot Jan 30, 2026
8ef5371
Simplify pagination cursor to use only TaskId since UUID v7 is monoto…
Copilot Jan 30, 2026
ef86c4d
Rename polyfill to CreateVersion7(DateTimeOffset) to match .NET 9 API
Copilot Jan 30, 2026
676aecb
Revert to CreateMonotonicUuid name and document why CreateVersion7 ca…
Copilot Jan 30, 2026
0877589
Refactor GuidPolyfills to GuidHelpers with simplified monotonic counter
Copilot Jan 30, 2026
7ea4064
Simplify GUID creation using unsafe pointer manipulation
Copilot Jan 30, 2026
e17be78
Fix test project duplicate compile for GuidHelpers.cs on net472
Copilot Jan 30, 2026
a9f8a4d
Scope unsafe block to only the pointer manipulation lines
Copilot Jan 30, 2026
446477d
Clarify comment that this is UUIDv7-like format, not RFC-compliant
Copilot Jan 30, 2026
0854883
Move AllowUnsafeBlocks to specific project csproj files
Copilot Jan 30, 2026
fe42850
Use two longs (ticks + counter) for monotonic ID generation instead o…
Copilot Jan 30, 2026
5dc6a21
Rename GuidHelpers to IdHelpers since it no longer generates GUIDs
Copilot Jan 30, 2026
14d0d82
Update src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj
stephentoub Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/ModelContextProtocol.Core/Server/InMemoryMcpTaskStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ namespace ModelContextProtocol;
[Experimental(Experimentals.Tasks_DiagnosticId, UrlFormat = Experimentals.Tasks_Url)]
public sealed class InMemoryMcpTaskStore : IMcpTaskStore, IDisposable
{
// Counter used for generating monotonically increasing task IDs, ensuring chronological ordering
// even when tasks are created within the same millisecond.
private static long s_taskIdCounter;

private readonly ConcurrentDictionary<string, TaskEntry> _tasks = new();
private readonly TimeSpan? _defaultTtl;
private readonly TimeSpan? _maxTtl;
Expand Down Expand Up @@ -179,7 +183,7 @@ public Task<McpTask> CreateTaskAsync(

if (!_tasks.TryAdd(taskId, entry))
{
// This should be extremely rare with GUID-based IDs
// Should never happen with counter-based IDs since each counter value is unique
throw new InvalidOperationException($"Task ID collision: {taskId}");
}

Expand Down Expand Up @@ -417,7 +421,15 @@ public void Dispose()
_cleanupTimer?.Dispose();
}

private static string GenerateTaskId() => Guid.NewGuid().ToString("N");
private string GenerateTaskId()
Comment thread
stephentoub marked this conversation as resolved.
Outdated
{
// Use Interlocked.Increment to generate a monotonically increasing counter.
// This ensures task IDs maintain chronological ordering for keyset pagination,
// even when multiple tasks are created within the same millisecond.
// Format: {counter:D20}-{uniqueSuffix} where D20 ensures lexicographic sorting.
long counter = Interlocked.Increment(ref s_taskIdCounter);
Comment thread
eiriktsarpalis marked this conversation as resolved.
Outdated
return $"{counter:D20}-{Guid.NewGuid():N}";
}

private static bool IsTerminalStatus(McpTaskStatus status) =>
status is McpTaskStatus.Completed or McpTaskStatus.Failed or McpTaskStatus.Cancelled;
Expand Down