Skip to content

Commit 3368bb7

Browse files
CopilotPranavSenthilnathanhalter73Copilot
authored
Use TimeToLive (TimeSpan?) instead of TtlMs (long?) in public APIs (#1644)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: PranavSenthilnathan <12225508+PranavSenthilnathan@users.noreply.github.com> Co-authored-by: halter73 <54385+halter73@users.noreply.github.com> Co-authored-by: Pranav Senthilnathan <pranas@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9ba1f47 commit 3368bb7

10 files changed

Lines changed: 30 additions & 85 deletions

File tree

src/ModelContextProtocol.Core/CompatibilitySuppressions.xml

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -827,13 +827,6 @@
827827
<Right>lib/net10.0/ModelContextProtocol.Core.dll</Right>
828828
<IsBaselineSuppression>true</IsBaselineSuppression>
829829
</Suppression>
830-
<Suppression>
831-
<DiagnosticId>CP0002</DiagnosticId>
832-
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.get_TimeToLive</Target>
833-
<Left>lib/net10.0/ModelContextProtocol.Core.dll</Left>
834-
<Right>lib/net10.0/ModelContextProtocol.Core.dll</Right>
835-
<IsBaselineSuppression>true</IsBaselineSuppression>
836-
</Suppression>
837830
<Suppression>
838831
<DiagnosticId>CP0002</DiagnosticId>
839832
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.set_PollInterval(System.Nullable{System.TimeSpan})</Target>
@@ -848,13 +841,6 @@
848841
<Right>lib/net10.0/ModelContextProtocol.Core.dll</Right>
849842
<IsBaselineSuppression>true</IsBaselineSuppression>
850843
</Suppression>
851-
<Suppression>
852-
<DiagnosticId>CP0002</DiagnosticId>
853-
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.set_TimeToLive(System.Nullable{System.TimeSpan})</Target>
854-
<Left>lib/net10.0/ModelContextProtocol.Core.dll</Left>
855-
<Right>lib/net10.0/ModelContextProtocol.Core.dll</Right>
856-
<IsBaselineSuppression>true</IsBaselineSuppression>
857-
</Suppression>
858844
<Suppression>
859845
<DiagnosticId>CP0002</DiagnosticId>
860846
<Target>M:ModelContextProtocol.Protocol.ServerCapabilities.get_Tasks</Target>
@@ -1212,13 +1198,6 @@
12121198
<Right>lib/net8.0/ModelContextProtocol.Core.dll</Right>
12131199
<IsBaselineSuppression>true</IsBaselineSuppression>
12141200
</Suppression>
1215-
<Suppression>
1216-
<DiagnosticId>CP0002</DiagnosticId>
1217-
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.get_TimeToLive</Target>
1218-
<Left>lib/net8.0/ModelContextProtocol.Core.dll</Left>
1219-
<Right>lib/net8.0/ModelContextProtocol.Core.dll</Right>
1220-
<IsBaselineSuppression>true</IsBaselineSuppression>
1221-
</Suppression>
12221201
<Suppression>
12231202
<DiagnosticId>CP0002</DiagnosticId>
12241203
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.set_PollInterval(System.Nullable{System.TimeSpan})</Target>
@@ -1233,13 +1212,6 @@
12331212
<Right>lib/net8.0/ModelContextProtocol.Core.dll</Right>
12341213
<IsBaselineSuppression>true</IsBaselineSuppression>
12351214
</Suppression>
1236-
<Suppression>
1237-
<DiagnosticId>CP0002</DiagnosticId>
1238-
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.set_TimeToLive(System.Nullable{System.TimeSpan})</Target>
1239-
<Left>lib/net8.0/ModelContextProtocol.Core.dll</Left>
1240-
<Right>lib/net8.0/ModelContextProtocol.Core.dll</Right>
1241-
<IsBaselineSuppression>true</IsBaselineSuppression>
1242-
</Suppression>
12431215
<Suppression>
12441216
<DiagnosticId>CP0002</DiagnosticId>
12451217
<Target>M:ModelContextProtocol.Protocol.ServerCapabilities.get_Tasks</Target>
@@ -1597,13 +1569,6 @@
15971569
<Right>lib/net9.0/ModelContextProtocol.Core.dll</Right>
15981570
<IsBaselineSuppression>true</IsBaselineSuppression>
15991571
</Suppression>
1600-
<Suppression>
1601-
<DiagnosticId>CP0002</DiagnosticId>
1602-
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.get_TimeToLive</Target>
1603-
<Left>lib/net9.0/ModelContextProtocol.Core.dll</Left>
1604-
<Right>lib/net9.0/ModelContextProtocol.Core.dll</Right>
1605-
<IsBaselineSuppression>true</IsBaselineSuppression>
1606-
</Suppression>
16071572
<Suppression>
16081573
<DiagnosticId>CP0002</DiagnosticId>
16091574
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.set_PollInterval(System.Nullable{System.TimeSpan})</Target>
@@ -1618,13 +1583,6 @@
16181583
<Right>lib/net9.0/ModelContextProtocol.Core.dll</Right>
16191584
<IsBaselineSuppression>true</IsBaselineSuppression>
16201585
</Suppression>
1621-
<Suppression>
1622-
<DiagnosticId>CP0002</DiagnosticId>
1623-
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.set_TimeToLive(System.Nullable{System.TimeSpan})</Target>
1624-
<Left>lib/net9.0/ModelContextProtocol.Core.dll</Left>
1625-
<Right>lib/net9.0/ModelContextProtocol.Core.dll</Right>
1626-
<IsBaselineSuppression>true</IsBaselineSuppression>
1627-
</Suppression>
16281586
<Suppression>
16291587
<DiagnosticId>CP0002</DiagnosticId>
16301588
<Target>M:ModelContextProtocol.Protocol.ServerCapabilities.get_Tasks</Target>
@@ -1982,13 +1940,6 @@
19821940
<Right>lib/netstandard2.0/ModelContextProtocol.Core.dll</Right>
19831941
<IsBaselineSuppression>true</IsBaselineSuppression>
19841942
</Suppression>
1985-
<Suppression>
1986-
<DiagnosticId>CP0002</DiagnosticId>
1987-
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.get_TimeToLive</Target>
1988-
<Left>lib/netstandard2.0/ModelContextProtocol.Core.dll</Left>
1989-
<Right>lib/netstandard2.0/ModelContextProtocol.Core.dll</Right>
1990-
<IsBaselineSuppression>true</IsBaselineSuppression>
1991-
</Suppression>
19921943
<Suppression>
19931944
<DiagnosticId>CP0002</DiagnosticId>
19941945
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.set_PollInterval(System.Nullable{System.TimeSpan})</Target>
@@ -2003,13 +1954,6 @@
20031954
<Right>lib/netstandard2.0/ModelContextProtocol.Core.dll</Right>
20041955
<IsBaselineSuppression>true</IsBaselineSuppression>
20051956
</Suppression>
2006-
<Suppression>
2007-
<DiagnosticId>CP0002</DiagnosticId>
2008-
<Target>M:ModelContextProtocol.Protocol.GetTaskResult.set_TimeToLive(System.Nullable{System.TimeSpan})</Target>
2009-
<Left>lib/netstandard2.0/ModelContextProtocol.Core.dll</Left>
2010-
<Right>lib/netstandard2.0/ModelContextProtocol.Core.dll</Right>
2011-
<IsBaselineSuppression>true</IsBaselineSuppression>
2012-
</Suppression>
20131957
<Suppression>
20141958
<DiagnosticId>CP0002</DiagnosticId>
20151959
<Target>M:ModelContextProtocol.Protocol.ServerCapabilities.get_Tasks</Target>

src/ModelContextProtocol.Core/Protocol/CreateTaskResult.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ public sealed class CreateTaskResult : Result
5555
public required DateTimeOffset LastUpdatedAt { get; set; }
5656

5757
/// <summary>
58-
/// Gets or sets the time-to-live duration from creation in milliseconds, or <see langword="null"/> for unlimited.
58+
/// Gets or sets the time-to-live duration from creation, or <see langword="null"/> for unlimited.
5959
/// </summary>
6060
[JsonPropertyName("ttlMs")]
61-
public long? TtlMs { get; set; }
61+
[JsonConverter(typeof(TimeSpanMillisecondsConverter))]
62+
public TimeSpan? TimeToLive { get; set; }
6263

6364
/// <summary>
6465
/// Gets or sets the suggested polling interval in milliseconds.

src/ModelContextProtocol.Core/Protocol/GetTaskResult.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ private protected GetTaskResult()
6464
public required DateTimeOffset LastUpdatedAt { get; set; }
6565

6666
/// <summary>
67-
/// Gets or sets the time-to-live duration from creation in milliseconds, or <see langword="null"/> for unlimited.
67+
/// Gets or sets the time-to-live duration from creation, or <see langword="null"/> for unlimited.
6868
/// </summary>
6969
[JsonPropertyName("ttlMs")]
70-
public long? TtlMs { get; set; }
70+
public TimeSpan? TimeToLive { get; set; }
7171

7272
/// <summary>
7373
/// Gets or sets the suggested polling interval in milliseconds.
@@ -245,7 +245,7 @@ internal sealed class Converter : JsonConverter<GetTaskResult>
245245
};
246246

247247
taskResult.StatusMessage = statusMessage;
248-
taskResult.TtlMs = ttlMs;
248+
taskResult.TimeToLive = ttlMs is null ? null : TimeSpan.FromMilliseconds(ttlMs.Value);
249249
taskResult.PollIntervalMs = pollIntervalMs;
250250
taskResult.ResultType = resultType;
251251
taskResult.Meta = meta;
@@ -287,9 +287,9 @@ public override void Write(Utf8JsonWriter writer, GetTaskResult value, JsonSeria
287287
writer.WriteString("createdAt", value.CreatedAt);
288288
writer.WriteString("lastUpdatedAt", value.LastUpdatedAt);
289289

290-
if (value.TtlMs is not null)
290+
if (value.TimeToLive is not null)
291291
{
292-
writer.WriteNumber("ttlMs", value.TtlMs.Value);
292+
writer.WriteNumber("ttlMs", (long)value.TimeToLive.Value.TotalMilliseconds);
293293
}
294294

295295
if (value.PollIntervalMs is not null)

src/ModelContextProtocol.Core/Protocol/TaskStatusNotificationParams.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ private protected TaskStatusNotificationParams()
7070
public required DateTimeOffset LastUpdatedAt { get; set; }
7171

7272
/// <summary>
73-
/// Gets or sets the time-to-live duration from creation in milliseconds, or <see langword="null"/> for unlimited.
73+
/// Gets or sets the time-to-live duration from creation, or <see langword="null"/> for unlimited.
7474
/// </summary>
7575
[JsonPropertyName("ttlMs")]
76-
public long? TtlMs { get; set; }
76+
public TimeSpan? TimeToLive { get; set; }
7777

7878
/// <summary>
7979
/// Gets or sets the suggested polling interval in milliseconds.
@@ -247,7 +247,7 @@ internal sealed class Converter : JsonConverter<TaskStatusNotificationParams>
247247
};
248248

249249
notification.StatusMessage = statusMessage;
250-
notification.TtlMs = ttlMs;
250+
notification.TimeToLive = ttlMs is null ? null : TimeSpan.FromMilliseconds(ttlMs.Value);
251251
notification.PollIntervalMs = pollIntervalMs;
252252
notification.Meta = meta;
253253

@@ -283,9 +283,9 @@ public override void Write(Utf8JsonWriter writer, TaskStatusNotificationParams v
283283
writer.WriteString("createdAt", value.CreatedAt);
284284
writer.WriteString("lastUpdatedAt", value.LastUpdatedAt);
285285

286-
if (value.TtlMs is not null)
286+
if (value.TimeToLive is not null)
287287
{
288-
writer.WriteNumber("ttlMs", value.TtlMs.Value);
288+
writer.WriteNumber("ttlMs", (long)value.TimeToLive.Value.TotalMilliseconds);
289289
}
290290

291291
if (value.PollIntervalMs is not null)

src/ModelContextProtocol.Core/Server/InMemoryMcpTaskStore.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,17 @@ public class InMemoryMcpTaskStore : IMcpTaskStore
3232
public long DefaultPollIntervalMs { get; set; } = 1000;
3333

3434
/// <summary>
35-
/// Gets or sets the default time-to-live in milliseconds for new tasks, or <see langword="null"/> for unlimited.
35+
/// Gets or sets the default time-to-live for new tasks, or <see langword="null"/> for unlimited.
3636
/// </summary>
37-
public long? DefaultTtlMs { get; set; }
37+
public TimeSpan? DefaultTimeToLive { get; set; }
3838

3939
/// <inheritdoc/>
4040
public Task<McpTaskInfo> CreateTaskAsync(CancellationToken cancellationToken = default)
4141
{
4242
var taskId = Guid.NewGuid().ToString("N");
4343
var now = DateTimeOffset.UtcNow;
4444

45-
var info = new McpTaskInfo(taskId, McpTaskStatus.Working, now, now, DefaultTtlMs, DefaultPollIntervalMs);
45+
var info = new McpTaskInfo(taskId, McpTaskStatus.Working, now, now, DefaultTimeToLive, DefaultPollIntervalMs);
4646
_tasks[taskId] = info;
4747

4848
return Task.FromResult(info);

src/ModelContextProtocol.Core/Server/McpServerImpl.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,7 @@ await originalListToolsHandler(request, cancellationToken).ConfigureAwait(false)
10231023
Status = info.Status,
10241024
CreatedAt = info.CreatedAt,
10251025
LastUpdatedAt = info.LastUpdatedAt,
1026-
TtlMs = info.TtlMs,
1026+
TimeToLive = info.TimeToLive,
10271027
PollIntervalMs = info.PollIntervalMs,
10281028
StatusMessage = info.StatusMessage,
10291029
ResultType = "task",
@@ -1036,7 +1036,7 @@ await originalListToolsHandler(request, cancellationToken).ConfigureAwait(false)
10361036
TaskId = info.TaskId,
10371037
CreatedAt = info.CreatedAt,
10381038
LastUpdatedAt = info.LastUpdatedAt,
1039-
TtlMs = info.TtlMs,
1039+
TimeToLive = info.TimeToLive,
10401040
PollIntervalMs = info.PollIntervalMs,
10411041
StatusMessage = info.StatusMessage,
10421042
ResultType = "complete",
@@ -1046,7 +1046,7 @@ await originalListToolsHandler(request, cancellationToken).ConfigureAwait(false)
10461046
TaskId = info.TaskId,
10471047
CreatedAt = info.CreatedAt,
10481048
LastUpdatedAt = info.LastUpdatedAt,
1049-
TtlMs = info.TtlMs,
1049+
TimeToLive = info.TimeToLive,
10501050
PollIntervalMs = info.PollIntervalMs,
10511051
StatusMessage = info.StatusMessage,
10521052
Result = info.Result ?? throw new InvalidOperationException($"Task '{info.TaskId}' is completed but has no result."),
@@ -1057,7 +1057,7 @@ await originalListToolsHandler(request, cancellationToken).ConfigureAwait(false)
10571057
TaskId = info.TaskId,
10581058
CreatedAt = info.CreatedAt,
10591059
LastUpdatedAt = info.LastUpdatedAt,
1060-
TtlMs = info.TtlMs,
1060+
TimeToLive = info.TimeToLive,
10611061
PollIntervalMs = info.PollIntervalMs,
10621062
StatusMessage = info.StatusMessage,
10631063
Error = info.Error ?? throw new InvalidOperationException($"Task '{info.TaskId}' is failed but has no error."),
@@ -1068,7 +1068,7 @@ await originalListToolsHandler(request, cancellationToken).ConfigureAwait(false)
10681068
TaskId = info.TaskId,
10691069
CreatedAt = info.CreatedAt,
10701070
LastUpdatedAt = info.LastUpdatedAt,
1071-
TtlMs = info.TtlMs,
1071+
TimeToLive = info.TimeToLive,
10721072
PollIntervalMs = info.PollIntervalMs,
10731073
StatusMessage = info.StatusMessage,
10741074
ResultType = "complete",
@@ -1078,7 +1078,7 @@ await originalListToolsHandler(request, cancellationToken).ConfigureAwait(false)
10781078
TaskId = info.TaskId,
10791079
CreatedAt = info.CreatedAt,
10801080
LastUpdatedAt = info.LastUpdatedAt,
1081-
TtlMs = info.TtlMs,
1081+
TimeToLive = info.TimeToLive,
10821082
PollIntervalMs = info.PollIntervalMs,
10831083
StatusMessage = info.StatusMessage,
10841084
// McpTaskInfo.InputRequests is IReadOnlyDictionary (covers immutable store

src/ModelContextProtocol.Core/Server/McpTaskInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public sealed record McpTaskInfo(
2020
McpTaskStatus Status,
2121
DateTimeOffset CreatedAt,
2222
DateTimeOffset LastUpdatedAt,
23-
long? TtlMs = null,
23+
TimeSpan? TimeToLive = null,
2424
long? PollIntervalMs = null,
2525
string? StatusMessage = null,
2626
JsonElement? Result = null,

tests/ModelContextProtocol.Tests/Protocol/TaskSerializationTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public static void CreateTaskResult_SerializationRoundTrip_PreservesAllPropertie
2121
StatusMessage = "Processing...",
2222
CreatedAt = new DateTimeOffset(2025, 6, 1, 12, 0, 0, TimeSpan.Zero),
2323
LastUpdatedAt = new DateTimeOffset(2025, 6, 1, 12, 5, 0, TimeSpan.Zero),
24-
TtlMs = 3600000,
24+
TimeToLive = TimeSpan.FromHours(1),
2525
PollIntervalMs = 5000,
2626
ResultType = "task",
2727
Meta = new JsonObject { ["key"] = "value" }
@@ -36,7 +36,7 @@ public static void CreateTaskResult_SerializationRoundTrip_PreservesAllPropertie
3636
Assert.Equal("Processing...", deserialized.StatusMessage);
3737
Assert.Equal(original.CreatedAt, deserialized.CreatedAt);
3838
Assert.Equal(original.LastUpdatedAt, deserialized.LastUpdatedAt);
39-
Assert.Equal(3600000, deserialized.TtlMs);
39+
Assert.Equal(TimeSpan.FromHours(1), deserialized.TimeToLive);
4040
Assert.Equal(5000, deserialized.PollIntervalMs);
4141
Assert.Equal("task", deserialized.ResultType);
4242
Assert.NotNull(deserialized.Meta);
@@ -52,7 +52,7 @@ public static void CreateTaskResult_UsesCorrectWireFieldNames()
5252
Status = McpTaskStatus.Working,
5353
CreatedAt = DateTimeOffset.UtcNow,
5454
LastUpdatedAt = DateTimeOffset.UtcNow,
55-
TtlMs = 60000,
55+
TimeToLive = TimeSpan.FromMinutes(1),
5656
PollIntervalMs = 1000,
5757
ResultType = "task",
5858
};

tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ public async Task CreateTaskAsync_UsesDefaultPollInterval()
5555
}
5656

5757
[Fact]
58-
public async Task CreateTaskAsync_UsesDefaultTtl()
58+
public async Task CreateTaskAsync_UsesDefaultTimeToLive()
5959
{
60-
var store = new InMemoryMcpTaskStore { DefaultTtlMs = 30000 };
60+
var store = new InMemoryMcpTaskStore { DefaultTimeToLive = TimeSpan.FromSeconds(30) };
6161

6262
var result = await store.CreateTaskAsync(CT);
6363

64-
Assert.Equal(30000, result.TtlMs);
64+
Assert.Equal(TimeSpan.FromSeconds(30), result.TimeToLive);
6565
}
6666

6767
[Fact]

tests/ModelContextProtocol.Tests/Server/TaskCancellationIntegrationTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer
3434
options.TaskStore = new InMemoryMcpTaskStore
3535
{
3636
DefaultPollIntervalMs = 50,
37-
DefaultTtlMs = 5000,
37+
DefaultTimeToLive = TimeSpan.FromSeconds(5),
3838
};
3939
});
4040

0 commit comments

Comments
 (0)