diff --git a/src/ModelContextProtocol.Core/CompatibilitySuppressions.xml b/src/ModelContextProtocol.Core/CompatibilitySuppressions.xml index 9943993..e686b45 100644 --- a/src/ModelContextProtocol.Core/CompatibilitySuppressions.xml +++ b/src/ModelContextProtocol.Core/CompatibilitySuppressions.xml @@ -827,13 +827,6 @@ lib/net10.0/ModelContextProtocol.Core.dll true - - CP0002 - M:ModelContextProtocol.Protocol.GetTaskResult.get_TimeToLive - lib/net10.0/ModelContextProtocol.Core.dll - lib/net10.0/ModelContextProtocol.Core.dll - true - CP0002 M:ModelContextProtocol.Protocol.GetTaskResult.set_PollInterval(System.Nullable{System.TimeSpan}) @@ -848,13 +841,6 @@ lib/net10.0/ModelContextProtocol.Core.dll true - - CP0002 - M:ModelContextProtocol.Protocol.GetTaskResult.set_TimeToLive(System.Nullable{System.TimeSpan}) - lib/net10.0/ModelContextProtocol.Core.dll - lib/net10.0/ModelContextProtocol.Core.dll - true - CP0002 M:ModelContextProtocol.Protocol.ServerCapabilities.get_Tasks @@ -1212,13 +1198,6 @@ lib/net8.0/ModelContextProtocol.Core.dll true - - CP0002 - M:ModelContextProtocol.Protocol.GetTaskResult.get_TimeToLive - lib/net8.0/ModelContextProtocol.Core.dll - lib/net8.0/ModelContextProtocol.Core.dll - true - CP0002 M:ModelContextProtocol.Protocol.GetTaskResult.set_PollInterval(System.Nullable{System.TimeSpan}) @@ -1233,13 +1212,6 @@ lib/net8.0/ModelContextProtocol.Core.dll true - - CP0002 - M:ModelContextProtocol.Protocol.GetTaskResult.set_TimeToLive(System.Nullable{System.TimeSpan}) - lib/net8.0/ModelContextProtocol.Core.dll - lib/net8.0/ModelContextProtocol.Core.dll - true - CP0002 M:ModelContextProtocol.Protocol.ServerCapabilities.get_Tasks @@ -1597,13 +1569,6 @@ lib/net9.0/ModelContextProtocol.Core.dll true - - CP0002 - M:ModelContextProtocol.Protocol.GetTaskResult.get_TimeToLive - lib/net9.0/ModelContextProtocol.Core.dll - lib/net9.0/ModelContextProtocol.Core.dll - true - CP0002 M:ModelContextProtocol.Protocol.GetTaskResult.set_PollInterval(System.Nullable{System.TimeSpan}) @@ -1618,13 +1583,6 @@ lib/net9.0/ModelContextProtocol.Core.dll true - - CP0002 - M:ModelContextProtocol.Protocol.GetTaskResult.set_TimeToLive(System.Nullable{System.TimeSpan}) - lib/net9.0/ModelContextProtocol.Core.dll - lib/net9.0/ModelContextProtocol.Core.dll - true - CP0002 M:ModelContextProtocol.Protocol.ServerCapabilities.get_Tasks @@ -1982,13 +1940,6 @@ lib/netstandard2.0/ModelContextProtocol.Core.dll true - - CP0002 - M:ModelContextProtocol.Protocol.GetTaskResult.get_TimeToLive - lib/netstandard2.0/ModelContextProtocol.Core.dll - lib/netstandard2.0/ModelContextProtocol.Core.dll - true - CP0002 M:ModelContextProtocol.Protocol.GetTaskResult.set_PollInterval(System.Nullable{System.TimeSpan}) @@ -2003,13 +1954,6 @@ lib/netstandard2.0/ModelContextProtocol.Core.dll true - - CP0002 - M:ModelContextProtocol.Protocol.GetTaskResult.set_TimeToLive(System.Nullable{System.TimeSpan}) - lib/netstandard2.0/ModelContextProtocol.Core.dll - lib/netstandard2.0/ModelContextProtocol.Core.dll - true - CP0002 M:ModelContextProtocol.Protocol.ServerCapabilities.get_Tasks diff --git a/src/ModelContextProtocol.Core/Protocol/CreateTaskResult.cs b/src/ModelContextProtocol.Core/Protocol/CreateTaskResult.cs index 2e5bc0c..6d9bac2 100644 --- a/src/ModelContextProtocol.Core/Protocol/CreateTaskResult.cs +++ b/src/ModelContextProtocol.Core/Protocol/CreateTaskResult.cs @@ -55,10 +55,11 @@ public sealed class CreateTaskResult : Result public required DateTimeOffset LastUpdatedAt { get; set; } /// - /// Gets or sets the time-to-live duration from creation in milliseconds, or for unlimited. + /// Gets or sets the time-to-live duration from creation, or for unlimited. /// [JsonPropertyName("ttlMs")] - public long? TtlMs { get; set; } + [JsonConverter(typeof(TimeSpanMillisecondsConverter))] + public TimeSpan? TimeToLive { get; set; } /// /// Gets or sets the suggested polling interval in milliseconds. diff --git a/src/ModelContextProtocol.Core/Protocol/GetTaskResult.cs b/src/ModelContextProtocol.Core/Protocol/GetTaskResult.cs index 9366f23..3fea8cc 100644 --- a/src/ModelContextProtocol.Core/Protocol/GetTaskResult.cs +++ b/src/ModelContextProtocol.Core/Protocol/GetTaskResult.cs @@ -64,10 +64,10 @@ public abstract class GetTaskResult : Result public required DateTimeOffset LastUpdatedAt { get; set; } /// - /// Gets or sets the time-to-live duration from creation in milliseconds, or for unlimited. + /// Gets or sets the time-to-live duration from creation, or for unlimited. /// [JsonPropertyName("ttlMs")] - public long? TtlMs { get; set; } + public TimeSpan? TimeToLive { get; set; } /// /// Gets or sets the suggested polling interval in milliseconds. @@ -245,7 +245,7 @@ public abstract class GetTaskResult : Result }; taskResult.StatusMessage = statusMessage; - taskResult.TtlMs = ttlMs; + taskResult.TimeToLive = ttlMs is null ? null : TimeSpan.FromMilliseconds(ttlMs.Value); taskResult.PollIntervalMs = pollIntervalMs; taskResult.ResultType = resultType; taskResult.Meta = meta; @@ -287,9 +287,9 @@ public abstract class GetTaskResult : Result writer.WriteString("createdAt", value.CreatedAt); writer.WriteString("lastUpdatedAt", value.LastUpdatedAt); - if (value.TtlMs is not null) + if (value.TimeToLive is not null) { - writer.WriteNumber("ttlMs", value.TtlMs.Value); + writer.WriteNumber("ttlMs", (long)value.TimeToLive.Value.TotalMilliseconds); } if (value.PollIntervalMs is not null) diff --git a/src/ModelContextProtocol.Core/Protocol/TaskStatusNotificationParams.cs b/src/ModelContextProtocol.Core/Protocol/TaskStatusNotificationParams.cs index e1bbb2f..4e859a2 100644 --- a/src/ModelContextProtocol.Core/Protocol/TaskStatusNotificationParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/TaskStatusNotificationParams.cs @@ -70,10 +70,10 @@ public abstract class TaskStatusNotificationParams : NotificationParams public required DateTimeOffset LastUpdatedAt { get; set; } /// - /// Gets or sets the time-to-live duration from creation in milliseconds, or for unlimited. + /// Gets or sets the time-to-live duration from creation, or for unlimited. /// [JsonPropertyName("ttlMs")] - public long? TtlMs { get; set; } + public TimeSpan? TimeToLive { get; set; } /// /// Gets or sets the suggested polling interval in milliseconds. @@ -247,7 +247,7 @@ public abstract class TaskStatusNotificationParams : NotificationParams }; notification.StatusMessage = statusMessage; - notification.TtlMs = ttlMs; + notification.TimeToLive = ttlMs is null ? null : TimeSpan.FromMilliseconds(ttlMs.Value); notification.PollIntervalMs = pollIntervalMs; notification.Meta = meta; @@ -283,9 +283,9 @@ public abstract class TaskStatusNotificationParams : NotificationParams writer.WriteString("createdAt", value.CreatedAt); writer.WriteString("lastUpdatedAt", value.LastUpdatedAt); - if (value.TtlMs is not null) + if (value.TimeToLive is not null) { - writer.WriteNumber("ttlMs", value.TtlMs.Value); + writer.WriteNumber("ttlMs", (long)value.TimeToLive.Value.TotalMilliseconds); } if (value.PollIntervalMs is not null) diff --git a/src/ModelContextProtocol.Core/Server/InMemoryMcpTaskStore.cs b/src/ModelContextProtocol.Core/Server/InMemoryMcpTaskStore.cs index a13af13..00c6984 100644 --- a/src/ModelContextProtocol.Core/Server/InMemoryMcpTaskStore.cs +++ b/src/ModelContextProtocol.Core/Server/InMemoryMcpTaskStore.cs @@ -32,9 +32,9 @@ public class InMemoryMcpTaskStore : IMcpTaskStore public long DefaultPollIntervalMs { get; set; } = 1000; /// - /// Gets or sets the default time-to-live in milliseconds for new tasks, or for unlimited. + /// Gets or sets the default time-to-live for new tasks, or for unlimited. /// - public long? DefaultTtlMs { get; set; } + public TimeSpan? DefaultTimeToLive { get; set; } /// public Task CreateTaskAsync(CancellationToken cancellationToken = default) @@ -42,7 +42,7 @@ public class InMemoryMcpTaskStore : IMcpTaskStore var taskId = Guid.NewGuid().ToString("N"); var now = DateTimeOffset.UtcNow; - var info = new McpTaskInfo(taskId, McpTaskStatus.Working, now, now, DefaultTtlMs, DefaultPollIntervalMs); + var info = new McpTaskInfo(taskId, McpTaskStatus.Working, now, now, DefaultTimeToLive, DefaultPollIntervalMs); _tasks[taskId] = info; return Task.FromResult(info); diff --git a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs index fc5a709..0a0721c 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs @@ -1023,7 +1023,7 @@ internal sealed partial class McpServerImpl : McpServer Status = info.Status, CreatedAt = info.CreatedAt, LastUpdatedAt = info.LastUpdatedAt, - TtlMs = info.TtlMs, + TimeToLive = info.TimeToLive, PollIntervalMs = info.PollIntervalMs, StatusMessage = info.StatusMessage, ResultType = "task", @@ -1036,7 +1036,7 @@ internal sealed partial class McpServerImpl : McpServer TaskId = info.TaskId, CreatedAt = info.CreatedAt, LastUpdatedAt = info.LastUpdatedAt, - TtlMs = info.TtlMs, + TimeToLive = info.TimeToLive, PollIntervalMs = info.PollIntervalMs, StatusMessage = info.StatusMessage, ResultType = "complete", @@ -1046,7 +1046,7 @@ internal sealed partial class McpServerImpl : McpServer TaskId = info.TaskId, CreatedAt = info.CreatedAt, LastUpdatedAt = info.LastUpdatedAt, - TtlMs = info.TtlMs, + TimeToLive = info.TimeToLive, PollIntervalMs = info.PollIntervalMs, StatusMessage = info.StatusMessage, Result = info.Result ?? throw new InvalidOperationException($"Task '{info.TaskId}' is completed but has no result."), @@ -1057,7 +1057,7 @@ internal sealed partial class McpServerImpl : McpServer TaskId = info.TaskId, CreatedAt = info.CreatedAt, LastUpdatedAt = info.LastUpdatedAt, - TtlMs = info.TtlMs, + TimeToLive = info.TimeToLive, PollIntervalMs = info.PollIntervalMs, StatusMessage = info.StatusMessage, Error = info.Error ?? throw new InvalidOperationException($"Task '{info.TaskId}' is failed but has no error."), @@ -1068,7 +1068,7 @@ internal sealed partial class McpServerImpl : McpServer TaskId = info.TaskId, CreatedAt = info.CreatedAt, LastUpdatedAt = info.LastUpdatedAt, - TtlMs = info.TtlMs, + TimeToLive = info.TimeToLive, PollIntervalMs = info.PollIntervalMs, StatusMessage = info.StatusMessage, ResultType = "complete", @@ -1078,7 +1078,7 @@ internal sealed partial class McpServerImpl : McpServer TaskId = info.TaskId, CreatedAt = info.CreatedAt, LastUpdatedAt = info.LastUpdatedAt, - TtlMs = info.TtlMs, + TimeToLive = info.TimeToLive, PollIntervalMs = info.PollIntervalMs, StatusMessage = info.StatusMessage, // McpTaskInfo.InputRequests is IReadOnlyDictionary (covers immutable store diff --git a/src/ModelContextProtocol.Core/Server/McpTaskInfo.cs b/src/ModelContextProtocol.Core/Server/McpTaskInfo.cs index ab71d65..87fa8ef 100644 --- a/src/ModelContextProtocol.Core/Server/McpTaskInfo.cs +++ b/src/ModelContextProtocol.Core/Server/McpTaskInfo.cs @@ -20,7 +20,7 @@ public sealed record McpTaskInfo( McpTaskStatus Status, DateTimeOffset CreatedAt, DateTimeOffset LastUpdatedAt, - long? TtlMs = null, + TimeSpan? TimeToLive = null, long? PollIntervalMs = null, string? StatusMessage = null, JsonElement? Result = null, diff --git a/tests/ModelContextProtocol.Tests/Protocol/TaskSerializationTests.cs b/tests/ModelContextProtocol.Tests/Protocol/TaskSerializationTests.cs index 556403a..86acc57 100644 --- a/tests/ModelContextProtocol.Tests/Protocol/TaskSerializationTests.cs +++ b/tests/ModelContextProtocol.Tests/Protocol/TaskSerializationTests.cs @@ -21,7 +21,7 @@ public static class TaskSerializationTests StatusMessage = "Processing...", CreatedAt = new DateTimeOffset(2025, 6, 1, 12, 0, 0, TimeSpan.Zero), LastUpdatedAt = new DateTimeOffset(2025, 6, 1, 12, 5, 0, TimeSpan.Zero), - TtlMs = 3600000, + TimeToLive = TimeSpan.FromHours(1), PollIntervalMs = 5000, ResultType = "task", Meta = new JsonObject { ["key"] = "value" } @@ -36,7 +36,7 @@ public static class TaskSerializationTests Assert.Equal("Processing...", deserialized.StatusMessage); Assert.Equal(original.CreatedAt, deserialized.CreatedAt); Assert.Equal(original.LastUpdatedAt, deserialized.LastUpdatedAt); - Assert.Equal(3600000, deserialized.TtlMs); + Assert.Equal(TimeSpan.FromHours(1), deserialized.TimeToLive); Assert.Equal(5000, deserialized.PollIntervalMs); Assert.Equal("task", deserialized.ResultType); Assert.NotNull(deserialized.Meta); @@ -52,7 +52,7 @@ public static class TaskSerializationTests Status = McpTaskStatus.Working, CreatedAt = DateTimeOffset.UtcNow, LastUpdatedAt = DateTimeOffset.UtcNow, - TtlMs = 60000, + TimeToLive = TimeSpan.FromMinutes(1), PollIntervalMs = 1000, ResultType = "task", }; diff --git a/tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs b/tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs index 7c87786..83afbfe 100644 --- a/tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs @@ -55,13 +55,13 @@ public class InMemoryMcpTaskStoreTests } [Fact] - public async Task CreateTaskAsync_UsesDefaultTtl() + public async Task CreateTaskAsync_UsesDefaultTimeToLive() { - var store = new InMemoryMcpTaskStore { DefaultTtlMs = 30000 }; + var store = new InMemoryMcpTaskStore { DefaultTimeToLive = TimeSpan.FromSeconds(30) }; var result = await store.CreateTaskAsync(CT); - Assert.Equal(30000, result.TtlMs); + Assert.Equal(TimeSpan.FromSeconds(30), result.TimeToLive); } [Fact] diff --git a/tests/ModelContextProtocol.Tests/Server/TaskCancellationIntegrationTests.cs b/tests/ModelContextProtocol.Tests/Server/TaskCancellationIntegrationTests.cs index 1b424d0..0e86933 100644 --- a/tests/ModelContextProtocol.Tests/Server/TaskCancellationIntegrationTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/TaskCancellationIntegrationTests.cs @@ -34,7 +34,7 @@ public class TaskCancellationIntegrationTests : ClientServerTestBase options.TaskStore = new InMemoryMcpTaskStore { DefaultPollIntervalMs = 50, - DefaultTtlMs = 5000, + DefaultTimeToLive = TimeSpan.FromSeconds(5), }; });