Implement SEP-2663 Tasks Extension (#1579)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Pranav Senthilnathan 2026-06-11 13:10:45 -07:00 • committed by GitHub
commit dbb7a20176
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 8190 additions and 11405 deletions

View file

@ -17,7 +17,6 @@ public static class CallToolRequestParamsTests
["city"] = JsonDocument.Parse("\"Seattle\"").RootElement.Clone(),
["units"] = JsonDocument.Parse("\"metric\"").RootElement.Clone()
},
Task = new McpTaskMetadata { TimeToLive = TimeSpan.FromHours(1) },
Meta = new JsonObject { ["progressToken"] = "token-123" }
};
@ -30,8 +29,6 @@ public static class CallToolRequestParamsTests
Assert.Equal(2, deserialized.Arguments.Count);
Assert.Equal("Seattle", deserialized.Arguments["city"].GetString());
Assert.Equal("metric", deserialized.Arguments["units"].GetString());
Assert.NotNull(deserialized.Task);
Assert.Equal(original.Task.TimeToLive, deserialized.Task.TimeToLive);
Assert.NotNull(deserialized.Meta);
Assert.Equal("token-123", (string)deserialized.Meta["progressToken"]!);
}
@ -50,7 +47,6 @@ public static class CallToolRequestParamsTests
Assert.NotNull(deserialized);
Assert.Equal(original.Name, deserialized.Name);
Assert.Null(deserialized.Arguments);
Assert.Null(deserialized.Task);
Assert.Null(deserialized.Meta);
}
}

View file

@ -14,13 +14,6 @@ public static class CallToolResultTests
Content = [new TextContentBlock { Text = "Result text" }],
StructuredContent = JsonElement.Parse("""{"temperature":72}"""),
IsError = false,
Task = new McpTask
{
TaskId = "task-1",
Status = McpTaskStatus.Completed,
CreatedAt = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
LastUpdatedAt = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero)
},
Meta = new JsonObject { ["key"] = "value" }
};
@ -34,8 +27,6 @@ public static class CallToolResultTests
Assert.NotNull(deserialized.StructuredContent);
Assert.Equal(72, deserialized.StructuredContent.Value.GetProperty("temperature").GetInt32());
Assert.False(deserialized.IsError);
Assert.NotNull(deserialized.Task);
Assert.Equal("task-1", deserialized.Task.TaskId);
Assert.NotNull(deserialized.Meta);
Assert.Equal("value", (string)deserialized.Meta["key"]!);
}
@ -52,7 +43,6 @@ public static class CallToolResultTests
Assert.Empty(deserialized.Content);
Assert.Null(deserialized.StructuredContent);
Assert.Null(deserialized.IsError);
Assert.Null(deserialized.Task);
Assert.Null(deserialized.Meta);
}
}

View file

@ -1,25 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class CancelMcpTaskRequestParamsTests
{
[Fact]
public static void CancelMcpTaskRequestParams_SerializationRoundTrip()
{
// Arrange
var original = new CancelMcpTaskRequestParams
{
TaskId = "cancel-task-456"
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<CancelMcpTaskRequestParams>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TaskId, deserialized.TaskId);
}
}

View file

@ -1,33 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class CancelMcpTaskResultTests
{
[Fact]
public static void CancelMcpTaskResult_SerializationRoundTrip()
{
// Arrange
var original = new CancelMcpTaskResult
{
TaskId = "cancelled-789",
Status = McpTaskStatus.Cancelled,
StatusMessage = "Cancelled by user",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
TimeToLive = null,
PollInterval = null
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<CancelMcpTaskResult>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TaskId, deserialized.TaskId);
Assert.Equal(original.Status, deserialized.Status);
Assert.Equal(original.StatusMessage, deserialized.StatusMessage);
}
}

View file

@ -21,7 +21,6 @@ public static class ClientCapabilitiesTests
Form = new FormElicitationCapability(),
Url = new UrlElicitationCapability()
},
Tasks = new McpTasksCapability(),
Extensions = new Dictionary<string, object>
{
["io.modelcontextprotocol/test"] = new object()
@ -40,7 +39,6 @@ public static class ClientCapabilitiesTests
Assert.NotNull(deserialized.Elicitation);
Assert.NotNull(deserialized.Elicitation.Form);
Assert.NotNull(deserialized.Elicitation.Url);
Assert.NotNull(deserialized.Tasks);
Assert.NotNull(deserialized.Extensions);
Assert.True(deserialized.Extensions.ContainsKey("io.modelcontextprotocol/test"));
}
@ -58,7 +56,6 @@ public static class ClientCapabilitiesTests
Assert.Null(deserialized.Roots);
Assert.Null(deserialized.Sampling);
Assert.Null(deserialized.Elicitation);
Assert.Null(deserialized.Tasks);
Assert.Null(deserialized.Extensions);
}

View file

@ -1,41 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace ModelContextProtocol.Tests.Protocol;
public static class CreateTaskResultTests
{
[Fact]
public static void CreateTaskResult_SerializationRoundTrip_PreservesAllProperties()
{
var original = new CreateTaskResult
{
Task = new McpTask
{
TaskId = "task-123",
Status = McpTaskStatus.Working,
StatusMessage = "Processing",
CreatedAt = new DateTimeOffset(2025, 6, 1, 12, 0, 0, TimeSpan.Zero),
LastUpdatedAt = new DateTimeOffset(2025, 6, 1, 12, 5, 0, TimeSpan.Zero),
TimeToLive = TimeSpan.FromHours(1),
PollInterval = TimeSpan.FromSeconds(5)
},
Meta = new JsonObject { ["key"] = "value" }
};
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<CreateTaskResult>(json, McpJsonUtilities.DefaultOptions);
Assert.NotNull(deserialized);
Assert.Equal("task-123", deserialized.Task.TaskId);
Assert.Equal(McpTaskStatus.Working, deserialized.Task.Status);
Assert.Equal("Processing", deserialized.Task.StatusMessage);
Assert.Equal(original.Task.CreatedAt, deserialized.Task.CreatedAt);
Assert.Equal(original.Task.LastUpdatedAt, deserialized.Task.LastUpdatedAt);
Assert.Equal(original.Task.TimeToLive, deserialized.Task.TimeToLive);
Assert.Equal(original.Task.PollInterval, deserialized.Task.PollInterval);
Assert.NotNull(deserialized.Meta);
Assert.Equal("value", (string)deserialized.Meta["key"]!);
}
}

View file

@ -23,7 +23,6 @@ public static class ElicitRequestParamsTests
["age"] = new ElicitRequestParams.NumberSchema { Description = "Your age" }
}
},
Task = new McpTaskMetadata { TimeToLive = TimeSpan.FromMinutes(10) },
Meta = new JsonObject { ["progressToken"] = "tok-1" }
};
@ -37,8 +36,6 @@ public static class ElicitRequestParamsTests
Assert.Equal("Please provide your details", deserialized.Message);
Assert.NotNull(deserialized.RequestedSchema);
Assert.Equal(2, deserialized.RequestedSchema.Properties.Count);
Assert.NotNull(deserialized.Task);
Assert.Equal(TimeSpan.FromMinutes(10), deserialized.Task.TimeToLive);
Assert.NotNull(deserialized.Meta);
Assert.Equal("tok-1", (string)deserialized.Meta["progressToken"]!);
}
@ -63,7 +60,6 @@ public static class ElicitRequestParamsTests
Assert.Equal("https://example.com/auth", deserialized.Url);
Assert.Equal("Please authenticate", deserialized.Message);
Assert.Null(deserialized.RequestedSchema);
Assert.Null(deserialized.Task);
}
[Fact]
@ -83,7 +79,6 @@ public static class ElicitRequestParamsTests
Assert.Null(deserialized.ElicitationId);
Assert.Null(deserialized.Url);
Assert.Null(deserialized.RequestedSchema);
Assert.Null(deserialized.Task);
Assert.Null(deserialized.Meta);
}
}

View file

@ -1,25 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class GetTaskPayloadRequestParamsTests
{
[Fact]
public static void GetTaskPayloadRequestParams_SerializationRoundTrip()
{
// Arrange
var original = new GetTaskPayloadRequestParams
{
TaskId = "payload-task-999"
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<GetTaskPayloadRequestParams>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TaskId, deserialized.TaskId);
}
}

View file

@ -1,25 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class GetTaskRequestParamsTests
{
[Fact]
public static void GetTaskRequestParams_SerializationRoundTrip()
{
// Arrange
var original = new GetTaskRequestParams
{
TaskId = "get-task-123"
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<GetTaskRequestParams>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TaskId, deserialized.TaskId);
}
}

View file

@ -1,37 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class GetTaskResultTests
{
[Fact]
public static void GetTaskResult_SerializationRoundTrip()
{
// Arrange
var original = new GetTaskResult
{
TaskId = "result-123",
Status = McpTaskStatus.Completed,
StatusMessage = "Done",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
TimeToLive = TimeSpan.FromHours(1),
PollInterval = TimeSpan.FromSeconds(1)
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TaskId, deserialized.TaskId);
Assert.Equal(original.Status, deserialized.Status);
Assert.Equal(original.StatusMessage, deserialized.StatusMessage);
Assert.Equal(original.CreatedAt, deserialized.CreatedAt);
Assert.Equal(original.LastUpdatedAt, deserialized.LastUpdatedAt);
Assert.Equal(original.TimeToLive, deserialized.TimeToLive);
Assert.Equal(original.PollInterval, deserialized.PollInterval);
}
}

View file

@ -1,25 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class ListTasksRequestParamsTests
{
[Fact]
public static void ListTasksRequestParams_SerializationRoundTrip()
{
// Arrange
var original = new ListTasksRequestParams
{
Cursor = "cursor-abc123"
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<ListTasksRequestParams>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.Cursor, deserialized.Cursor);
}
}

View file

@ -1,46 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class ListTasksResultTests
{
[Fact]
public static void ListTasksResult_SerializationRoundTrip()
{
// Arrange
var original = new ListTasksResult
{
Tasks =
[
new McpTask
{
TaskId = "task-1",
Status = McpTaskStatus.Working,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow
},
new McpTask
{
TaskId = "task-2",
Status = McpTaskStatus.Completed,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow
}
],
NextCursor = "next-page-token"
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<ListTasksResult>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.NotNull(deserialized.Tasks);
Assert.Equal(2, deserialized.Tasks.Count);
Assert.Equal(original.Tasks[0].TaskId, deserialized.Tasks[0].TaskId);
Assert.Equal(original.Tasks[1].TaskId, deserialized.Tasks[1].TaskId);
Assert.Equal(original.NextCursor, deserialized.NextCursor);
}
}

View file

@ -1,53 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class McpTaskMetadataTests
{
[Fact]
public static void McpTaskMetadata_SerializationRoundTrip_WithTimeToLive()
{
// Arrange
var original = new McpTaskMetadata
{
TimeToLive = TimeSpan.FromHours(2)
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<McpTaskMetadata>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TimeToLive, deserialized.TimeToLive);
}
[Fact]
public static void McpTaskMetadata_SerializationRoundTrip_WithNullTimeToLive()
{
// Arrange
var original = new McpTaskMetadata();
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<McpTaskMetadata>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Null(deserialized.TimeToLive);
}
[Fact]
public static void McpTaskMetadata_HasCorrectJsonPropertyNames()
{
var metadata = new McpTaskMetadata
{
TimeToLive = TimeSpan.FromMinutes(15)
};
string json = JsonSerializer.Serialize(metadata, McpJsonUtilities.DefaultOptions);
Assert.Contains("\"ttl\":", json);
}
}

View file

@ -1,37 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class McpTaskStatusNotificationParamsTests
{
[Fact]
public static void McpTaskStatusNotificationParams_SerializationRoundTrip()
{
// Arrange
var original = new McpTaskStatusNotificationParams
{
TaskId = "notification-task",
Status = McpTaskStatus.Completed,
StatusMessage = "Task completed successfully",
CreatedAt = new DateTimeOffset(2025, 12, 9, 10, 0, 0, TimeSpan.Zero),
LastUpdatedAt = new DateTimeOffset(2025, 12, 9, 10, 30, 0, TimeSpan.Zero),
TimeToLive = TimeSpan.FromHours(1),
PollInterval = TimeSpan.FromSeconds(2)
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<McpTaskStatusNotificationParams>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TaskId, deserialized.TaskId);
Assert.Equal(original.Status, deserialized.Status);
Assert.Equal(original.StatusMessage, deserialized.StatusMessage);
Assert.Equal(original.CreatedAt, deserialized.CreatedAt);
Assert.Equal(original.LastUpdatedAt, deserialized.LastUpdatedAt);
Assert.Equal(original.TimeToLive, deserialized.TimeToLive);
Assert.Equal(original.PollInterval, deserialized.PollInterval);
}
}

View file

@ -1,160 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class McpTaskTests
{
[Fact]
public static void McpTask_SerializationRoundTrip_PreservesAllProperties()
{
// Arrange
var original = new McpTask
{
TaskId = "task-12345",
Status = McpTaskStatus.Working,
StatusMessage = "Processing request",
CreatedAt = new DateTimeOffset(2025, 12, 9, 10, 30, 0, TimeSpan.Zero),
LastUpdatedAt = new DateTimeOffset(2025, 12, 9, 10, 35, 0, TimeSpan.Zero),
TimeToLive = TimeSpan.FromHours(24),
PollInterval = TimeSpan.FromSeconds(5)
};
// Act - Serialize to JSON
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
// Act - Deserialize back from JSON
var deserialized = JsonSerializer.Deserialize<McpTask>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TaskId, deserialized.TaskId);
Assert.Equal(original.Status, deserialized.Status);
Assert.Equal(original.StatusMessage, deserialized.StatusMessage);
Assert.Equal(original.CreatedAt, deserialized.CreatedAt);
Assert.Equal(original.LastUpdatedAt, deserialized.LastUpdatedAt);
Assert.Equal(original.TimeToLive, deserialized.TimeToLive);
Assert.Equal(original.PollInterval, deserialized.PollInterval);
}
[Fact]
public static void McpTask_SerializationRoundTrip_WithMinimalProperties()
{
// Arrange
var original = new McpTask
{
TaskId = "task-minimal",
Status = McpTaskStatus.Completed,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow
};
// Act - Serialize to JSON
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
// Act - Deserialize back from JSON
var deserialized = JsonSerializer.Deserialize<McpTask>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.TaskId, deserialized.TaskId);
Assert.Equal(original.Status, deserialized.Status);
Assert.Null(deserialized.StatusMessage);
Assert.Equal(original.CreatedAt, deserialized.CreatedAt);
Assert.Equal(original.LastUpdatedAt, deserialized.LastUpdatedAt);
Assert.Null(deserialized.TimeToLive);
Assert.Null(deserialized.PollInterval);
}
[Fact]
public static void McpTask_HasCorrectJsonPropertyNames()
{
var task = new McpTask
{
TaskId = "test-task",
Status = McpTaskStatus.Working,
StatusMessage = "Test message",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
TimeToLive = TimeSpan.FromMinutes(30),
PollInterval = TimeSpan.FromSeconds(1)
};
string json = JsonSerializer.Serialize(task, McpJsonUtilities.DefaultOptions);
Assert.Contains("\"taskId\":", json);
Assert.Contains("\"status\":", json);
Assert.Contains("\"statusMessage\":", json);
Assert.Contains("\"createdAt\":", json);
Assert.Contains("\"lastUpdatedAt\":", json);
Assert.Contains("\"ttl\":", json);
Assert.Contains("\"pollInterval\":", json);
}
[Fact]
public static void McpTask_TimeToLive_SerializesAsMilliseconds()
{
var task = new McpTask
{
TaskId = "test-ttl",
Status = McpTaskStatus.Working,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
TimeToLive = TimeSpan.FromSeconds(60)
};
string json = JsonSerializer.Serialize(task, McpJsonUtilities.DefaultOptions);
Assert.Contains("\"ttl\":60000", json);
}
[Theory]
[InlineData(McpTaskStatus.Working)]
[InlineData(McpTaskStatus.InputRequired)]
[InlineData(McpTaskStatus.Completed)]
[InlineData(McpTaskStatus.Failed)]
[InlineData(McpTaskStatus.Cancelled)]
public static void McpTaskStatus_SerializesCorrectly(McpTaskStatus status)
{
var task = new McpTask
{
TaskId = "status-test",
Status = status,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow
};
string json = JsonSerializer.Serialize(task, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<McpTask>(json, McpJsonUtilities.DefaultOptions);
Assert.NotNull(deserialized);
Assert.Equal(status, deserialized.Status);
}
[Fact]
public static void McpTaskStatus_HasCorrectJsonValues()
{
var statuses = new[]
{
(McpTaskStatus.Working, "working"),
(McpTaskStatus.InputRequired, "input_required"),
(McpTaskStatus.Completed, "completed"),
(McpTaskStatus.Failed, "failed"),
(McpTaskStatus.Cancelled, "cancelled")
};
foreach (var (status, expectedJson) in statuses)
{
var task = new McpTask
{
TaskId = "test",
Status = status,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow
};
string json = JsonSerializer.Serialize(task, McpJsonUtilities.DefaultOptions);
Assert.Contains($"\"status\":\"{expectedJson}\"", json);
}
}
}

View file

@ -1,91 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class McpTasksCapabilityTests
{
[Fact]
public static void McpTasksCapability_SerializationRoundTrip_WithAllProperties()
{
// Arrange
var original = new McpTasksCapability
{
List = new ListMcpTasksCapability(),
Cancel = new CancelMcpTasksCapability(),
Requests = new RequestMcpTasksCapability
{
Tools = new ToolsMcpTasksCapability
{
Call = new CallToolMcpTasksCapability()
},
Sampling = new SamplingMcpTasksCapability
{
CreateMessage = new CreateMessageMcpTasksCapability()
},
Elicitation = new ElicitationMcpTasksCapability
{
Create = new CreateElicitationMcpTasksCapability()
}
}
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<McpTasksCapability>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.NotNull(deserialized.List);
Assert.NotNull(deserialized.Cancel);
Assert.NotNull(deserialized.Requests);
Assert.NotNull(deserialized.Requests.Tools);
Assert.NotNull(deserialized.Requests.Tools.Call);
Assert.NotNull(deserialized.Requests.Sampling);
Assert.NotNull(deserialized.Requests.Sampling.CreateMessage);
Assert.NotNull(deserialized.Requests.Elicitation);
Assert.NotNull(deserialized.Requests.Elicitation.Create);
}
[Fact]
public static void McpTasksCapability_SerializationRoundTrip_WithMinimalProperties()
{
// Arrange
var original = new McpTasksCapability();
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<McpTasksCapability>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Null(deserialized.List);
Assert.Null(deserialized.Cancel);
Assert.Null(deserialized.Requests);
}
[Fact]
public static void McpTasksCapability_HasCorrectJsonPropertyNames()
{
var capability = new McpTasksCapability
{
List = new ListMcpTasksCapability(),
Cancel = new CancelMcpTasksCapability(),
Requests = new RequestMcpTasksCapability
{
Tools = new ToolsMcpTasksCapability
{
Call = new CallToolMcpTasksCapability()
}
}
};
string json = JsonSerializer.Serialize(capability, McpJsonUtilities.DefaultOptions);
Assert.Contains("\"list\":", json);
Assert.Contains("\"cancel\":", json);
Assert.Contains("\"requests\":", json);
Assert.Contains("\"tools\":", json);
Assert.Contains("\"call\":", json);
}
}

View file

@ -1,108 +0,0 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
namespace ModelContextProtocol.Tests.Protocol;
public static class RequestMcpTasksCapabilityTests
{
[Fact]
public static void RequestMcpTasksCapability_SerializationRoundTrip_ToolsOnly()
{
// Arrange
var original = new RequestMcpTasksCapability
{
Tools = new ToolsMcpTasksCapability
{
Call = new CallToolMcpTasksCapability()
}
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<RequestMcpTasksCapability>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.NotNull(deserialized.Tools);
Assert.NotNull(deserialized.Tools.Call);
Assert.Null(deserialized.Sampling);
Assert.Null(deserialized.Elicitation);
}
[Fact]
public static void RequestMcpTasksCapability_SerializationRoundTrip_SamplingOnly()
{
// Arrange
var original = new RequestMcpTasksCapability
{
Sampling = new SamplingMcpTasksCapability
{
CreateMessage = new CreateMessageMcpTasksCapability()
}
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<RequestMcpTasksCapability>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Null(deserialized.Tools);
Assert.NotNull(deserialized.Sampling);
Assert.NotNull(deserialized.Sampling.CreateMessage);
Assert.Null(deserialized.Elicitation);
}
[Fact]
public static void RequestMcpTasksCapability_SerializationRoundTrip_ElicitationOnly()
{
// Arrange
var original = new RequestMcpTasksCapability
{
Elicitation = new ElicitationMcpTasksCapability
{
Create = new CreateElicitationMcpTasksCapability()
}
};
// Act
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<RequestMcpTasksCapability>(json, McpJsonUtilities.DefaultOptions);
// Assert
Assert.NotNull(deserialized);
Assert.Null(deserialized.Tools);
Assert.Null(deserialized.Sampling);
Assert.NotNull(deserialized.Elicitation);
Assert.NotNull(deserialized.Elicitation.Create);
}
[Fact]
public static void RequestMcpTasksCapability_HasCorrectJsonPropertyNames()
{
var capability = new RequestMcpTasksCapability
{
Tools = new ToolsMcpTasksCapability
{
Call = new CallToolMcpTasksCapability()
},
Sampling = new SamplingMcpTasksCapability
{
CreateMessage = new CreateMessageMcpTasksCapability()
},
Elicitation = new ElicitationMcpTasksCapability
{
Create = new CreateElicitationMcpTasksCapability()
}
};
string json = JsonSerializer.Serialize(capability, McpJsonUtilities.DefaultOptions);
Assert.Contains("\"tools\":", json);
Assert.Contains("\"sampling\":", json);
Assert.Contains("\"elicitation\":", json);
Assert.Contains("\"call\":", json);
Assert.Contains("\"createMessage\":", json);
Assert.Contains("\"create\":", json);
}
}

View file

@ -15,7 +15,6 @@ public static class ServerCapabilitiesTests
Resources = new ResourcesCapability { Subscribe = true, ListChanged = true },
Tools = new ToolsCapability { ListChanged = false },
Completions = new CompletionsCapability(),
Tasks = new McpTasksCapability(),
Extensions = new Dictionary<string, object>
{
["io.modelcontextprotocol/apps"] = new object()
@ -35,7 +34,6 @@ public static class ServerCapabilitiesTests
Assert.NotNull(deserialized.Tools);
Assert.False(deserialized.Tools.ListChanged);
Assert.NotNull(deserialized.Completions);
Assert.NotNull(deserialized.Tasks);
Assert.NotNull(deserialized.Extensions);
Assert.True(deserialized.Extensions.ContainsKey("io.modelcontextprotocol/apps"));
}
@ -55,7 +53,6 @@ public static class ServerCapabilitiesTests
Assert.Null(deserialized.Resources);
Assert.Null(deserialized.Tools);
Assert.Null(deserialized.Completions);
Assert.Null(deserialized.Tasks);
Assert.Null(deserialized.Extensions);
}

View file

@ -0,0 +1,517 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace ModelContextProtocol.Tests.Protocol;
/// <summary>
/// Serialization and deserialization tests for SEP-2663 task protocol types.
/// </summary>
public static class TaskSerializationTests
{
#region CreateTaskResult
[Fact]
public static void CreateTaskResult_SerializationRoundTrip_PreservesAllProperties()
{
var original = new CreateTaskResult
{
TaskId = "task-123",
Status = McpTaskStatus.Working,
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,
PollIntervalMs = 5000,
ResultType = "task",
Meta = new JsonObject { ["key"] = "value" }
};
string json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<CreateTaskResult>(json, McpJsonUtilities.DefaultOptions);
Assert.NotNull(deserialized);
Assert.Equal("task-123", deserialized.TaskId);
Assert.Equal(McpTaskStatus.Working, deserialized.Status);
Assert.Equal("Processing...", deserialized.StatusMessage);
Assert.Equal(original.CreatedAt, deserialized.CreatedAt);
Assert.Equal(original.LastUpdatedAt, deserialized.LastUpdatedAt);
Assert.Equal(3600000, deserialized.TtlMs);
Assert.Equal(5000, deserialized.PollIntervalMs);
Assert.Equal("task", deserialized.ResultType);
Assert.NotNull(deserialized.Meta);
Assert.Equal("value", (string)deserialized.Meta["key"]!);
}
[Fact]
public static void CreateTaskResult_UsesCorrectWireFieldNames()
{
var result = new CreateTaskResult
{
TaskId = "t1",
Status = McpTaskStatus.Working,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
TtlMs = 60000,
PollIntervalMs = 1000,
ResultType = "task",
};
string json = JsonSerializer.Serialize(result, McpJsonUtilities.DefaultOptions);
// Must use camelCase wire names
Assert.Contains("\"ttlMs\":", json);
Assert.Contains("\"pollIntervalMs\":", json);
Assert.Contains("\"taskId\":", json);
Assert.Contains("\"resultType\":\"task\"", json);
// Must NOT contain legacy field names
Assert.DoesNotContain("\"ttl\":", json);
Assert.DoesNotContain("\"pollInterval\":", json);
}
[Fact]
public static void CreateTaskResult_ResultType_SerializesAsTask()
{
var result = new CreateTaskResult
{
TaskId = "t1",
Status = McpTaskStatus.Working,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
ResultType = "task",
};
string json = JsonSerializer.Serialize(result, McpJsonUtilities.DefaultOptions);
var node = JsonNode.Parse(json)!;
Assert.Equal("task", (string)node["resultType"]!);
}
#endregion
#region GetTaskResult Subtypes
[Fact]
public static void GetTaskResult_Working_RoundTrip()
{
var original = new WorkingTaskResult
{
TaskId = "w1",
CreatedAt = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
LastUpdatedAt = new DateTimeOffset(2025, 1, 1, 0, 1, 0, TimeSpan.Zero),
StatusMessage = "In progress",
PollIntervalMs = 2000,
};
string json = JsonSerializer.Serialize<GetTaskResult>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions);
var working = Assert.IsType<WorkingTaskResult>(deserialized);
Assert.Equal("w1", working.TaskId);
Assert.Equal(McpTaskStatus.Working, working.Status);
Assert.Equal("In progress", working.StatusMessage);
Assert.Equal(2000, working.PollIntervalMs);
}
[Fact]
public static void GetTaskResult_Completed_RoundTrip_IncludesResult()
{
var resultPayload = JsonElement.Parse("""{"content":[{"type":"text","text":"done"}]}""");
var original = new CompletedTaskResult
{
TaskId = "c1",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
Result = resultPayload,
};
string json = JsonSerializer.Serialize<GetTaskResult>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions);
var completed = Assert.IsType<CompletedTaskResult>(deserialized);
Assert.Equal("c1", completed.TaskId);
Assert.Equal(McpTaskStatus.Completed, completed.Status);
Assert.Equal(JsonValueKind.Object, completed.Result.ValueKind);
}
[Fact]
public static void GetTaskResult_Failed_RoundTrip_IncludesError()
{
var errorPayload = JsonElement.Parse("""{"code":-32000,"message":"internal error"}""");
var original = new FailedTaskResult
{
TaskId = "f1",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
Error = errorPayload,
};
string json = JsonSerializer.Serialize<GetTaskResult>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions);
var failed = Assert.IsType<FailedTaskResult>(deserialized);
Assert.Equal("f1", failed.TaskId);
Assert.Equal(McpTaskStatus.Failed, failed.Status);
Assert.Equal(-32000, failed.Error.GetProperty("code").GetInt32());
}
[Fact]
public static void GetTaskResult_Cancelled_RoundTrip()
{
var original = new CancelledTaskResult
{
TaskId = "x1",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
StatusMessage = "User cancelled",
};
string json = JsonSerializer.Serialize<GetTaskResult>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions);
var cancelled = Assert.IsType<CancelledTaskResult>(deserialized);
Assert.Equal("x1", cancelled.TaskId);
Assert.Equal(McpTaskStatus.Cancelled, cancelled.Status);
Assert.Equal("User cancelled", cancelled.StatusMessage);
}
[Fact]
public static void GetTaskResult_InputRequired_RoundTrip_IncludesInputRequests()
{
var inputRequests = new Dictionary<string, InputRequest>
{
["req-1"] = new InputRequest
{
Method = "elicitation/create",
Params = JsonElement.Parse("""{"message":"Confirm?"}"""),
}
};
var original = new InputRequiredTaskResult
{
TaskId = "i1",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
InputRequests = inputRequests,
};
string json = JsonSerializer.Serialize<GetTaskResult>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions);
var inputRequired = Assert.IsType<InputRequiredTaskResult>(deserialized);
Assert.Equal("i1", inputRequired.TaskId);
Assert.Equal(McpTaskStatus.InputRequired, inputRequired.Status);
Assert.NotNull(inputRequired.InputRequests);
Assert.Single(inputRequired.InputRequests);
Assert.True(inputRequired.InputRequests.ContainsKey("req-1"));
}
[Fact]
public static void GetTaskResult_Converter_DispatchesToCorrectSubtypeByStatus()
{
var statuses = new (string status, Type expectedType)[]
{
("working", typeof(WorkingTaskResult)),
("completed", typeof(CompletedTaskResult)),
("failed", typeof(FailedTaskResult)),
("cancelled", typeof(CancelledTaskResult)),
("input_required", typeof(InputRequiredTaskResult)),
};
foreach (var (status, expectedType) in statuses)
{
var json = status switch
{
"completed" => """{"taskId":"t","status":"completed","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z","result":{}}""",
"failed" => """{"taskId":"t","status":"failed","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z","error":{}}""",
"input_required" => """{"taskId":"t","status":"input_required","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z","inputRequests":{}}""",
_ => $$$"""{"taskId":"t","status":"{{{status}}}","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z"}""",
};
var result = JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
Assert.IsType(expectedType, result);
}
}
[Fact]
public static void GetTaskResult_MissingTaskId_ThrowsJsonException()
{
var json = """{"status":"working","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z"}""";
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions));
}
[Fact]
public static void GetTaskResult_MissingStatus_ThrowsJsonException()
{
var json = """{"taskId":"t","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z"}""";
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions));
}
[Fact]
public static void GetTaskResult_UnknownStatus_ThrowsJsonException()
{
var json = """{"taskId":"t","status":"exploded","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z"}""";
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions));
}
[Fact]
public static void GetTaskResult_CompletedMissingResult_ThrowsJsonException()
{
var json = """{"taskId":"t","status":"completed","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z"}""";
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions));
}
[Fact]
public static void GetTaskResult_FailedMissingError_ThrowsJsonException()
{
var json = """{"taskId":"t","status":"failed","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z"}""";
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions));
}
[Fact]
public static void GetTaskResult_InputRequiredMissingInputRequests_ThrowsJsonException()
{
var json = """{"taskId":"t","status":"input_required","createdAt":"2025-01-01T00:00:00Z","lastUpdatedAt":"2025-01-01T00:00:00Z"}""";
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<GetTaskResult>(json, McpJsonUtilities.DefaultOptions));
}
[Theory]
[InlineData(typeof(WorkingTaskResult))]
[InlineData(typeof(CompletedTaskResult))]
[InlineData(typeof(FailedTaskResult))]
[InlineData(typeof(CancelledTaskResult))]
[InlineData(typeof(InputRequiredTaskResult))]
public static void GetTaskResult_WireResultType_IsComplete_WhenSet(Type subType)
{
// SEP-2663: standard task responses (tasks/get, tasks/update, tasks/cancel) use resultType="complete".
var created = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
GetTaskResult value = subType switch
{
Type t when t == typeof(WorkingTaskResult) => new WorkingTaskResult { TaskId = "t", CreatedAt = created, LastUpdatedAt = created, ResultType = "complete" },
Type t when t == typeof(CompletedTaskResult) => new CompletedTaskResult { TaskId = "t", CreatedAt = created, LastUpdatedAt = created, Result = JsonElement.Parse("""{"ok":true}"""), ResultType = "complete" },
Type t when t == typeof(FailedTaskResult) => new FailedTaskResult { TaskId = "t", CreatedAt = created, LastUpdatedAt = created, Error = JsonElement.Parse("""{"code":-32603,"message":"boom"}"""), ResultType = "complete" },
Type t when t == typeof(CancelledTaskResult) => new CancelledTaskResult { TaskId = "t", CreatedAt = created, LastUpdatedAt = created, ResultType = "complete" },
Type t when t == typeof(InputRequiredTaskResult) => new InputRequiredTaskResult
{
TaskId = "t",
CreatedAt = created,
LastUpdatedAt = created,
ResultType = "complete",
InputRequests = new Dictionary<string, InputRequest>
{
["k"] = new InputRequest
{
Method = "test/method",
Params = JsonSerializer.SerializeToElement("ask", McpJsonUtilities.DefaultOptions),
},
},
},
_ => throw new InvalidOperationException()
};
string json = JsonSerializer.Serialize(value, McpJsonUtilities.DefaultOptions);
var node = JsonNode.Parse(json)!;
Assert.Equal("complete", (string?)node["resultType"]);
}
#endregion
#region McpTaskStatus Enum
[Theory]
[InlineData(McpTaskStatus.Working, "working")]
[InlineData(McpTaskStatus.InputRequired, "input_required")]
[InlineData(McpTaskStatus.Completed, "completed")]
[InlineData(McpTaskStatus.Cancelled, "cancelled")]
[InlineData(McpTaskStatus.Failed, "failed")]
public static void McpTaskStatus_SerializesAsSnakeCase(McpTaskStatus status, string expectedWireValue)
{
string json = JsonSerializer.Serialize(status, McpJsonUtilities.DefaultOptions);
Assert.Equal($"\"{expectedWireValue}\"", json);
var deserialized = JsonSerializer.Deserialize<McpTaskStatus>(json, McpJsonUtilities.DefaultOptions);
Assert.Equal(status, deserialized);
}
#endregion
#region TaskStatusNotificationParams
[Fact]
public static void TaskStatusNotificationParams_Working_RoundTrip()
{
var original = new WorkingTaskNotificationParams
{
TaskId = "n1",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
StatusMessage = "Working on it",
};
string json = JsonSerializer.Serialize<TaskStatusNotificationParams>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<TaskStatusNotificationParams>(json, McpJsonUtilities.DefaultOptions);
var working = Assert.IsType<WorkingTaskNotificationParams>(deserialized);
Assert.Equal("n1", working.TaskId);
Assert.Equal("Working on it", working.StatusMessage);
}
[Fact]
public static void TaskStatusNotificationParams_Completed_RoundTrip()
{
var resultPayload = JsonElement.Parse("""{"text":"done"}""");
var original = new CompletedTaskNotificationParams
{
TaskId = "n2",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
Result = resultPayload,
};
string json = JsonSerializer.Serialize<TaskStatusNotificationParams>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<TaskStatusNotificationParams>(json, McpJsonUtilities.DefaultOptions);
var completed = Assert.IsType<CompletedTaskNotificationParams>(deserialized);
Assert.Equal("n2", completed.TaskId);
Assert.Equal("done", completed.Result.GetProperty("text").GetString());
}
[Fact]
public static void TaskStatusNotificationParams_Failed_RoundTrip()
{
var errorPayload = JsonElement.Parse("""{"code":-1,"message":"boom"}""");
var original = new FailedTaskNotificationParams
{
TaskId = "n3",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
Error = errorPayload,
};
string json = JsonSerializer.Serialize<TaskStatusNotificationParams>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<TaskStatusNotificationParams>(json, McpJsonUtilities.DefaultOptions);
var failed = Assert.IsType<FailedTaskNotificationParams>(deserialized);
Assert.Equal("n3", failed.TaskId);
Assert.Equal("boom", failed.Error.GetProperty("message").GetString());
}
[Fact]
public static void TaskStatusNotificationParams_Cancelled_RoundTrip()
{
var original = new CancelledTaskNotificationParams
{
TaskId = "n4",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
};
string json = JsonSerializer.Serialize<TaskStatusNotificationParams>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<TaskStatusNotificationParams>(json, McpJsonUtilities.DefaultOptions);
Assert.IsType<CancelledTaskNotificationParams>(deserialized);
}
[Fact]
public static void TaskStatusNotificationParams_InputRequired_RoundTrip()
{
var inputRequests = new Dictionary<string, InputRequest>
{
["r1"] = new InputRequest { Method = "sampling/createMessage" }
};
var original = new InputRequiredTaskNotificationParams
{
TaskId = "n5",
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
InputRequests = inputRequests,
};
string json = JsonSerializer.Serialize<TaskStatusNotificationParams>(original, McpJsonUtilities.DefaultOptions);
var deserialized = JsonSerializer.Deserialize<TaskStatusNotificationParams>(json, McpJsonUtilities.DefaultOptions);
var inputRequired = Assert.IsType<InputRequiredTaskNotificationParams>(deserialized);
Assert.NotNull(inputRequired.InputRequests);
Assert.Single(inputRequired.InputRequests);
}
#endregion
#region ResultOrCreatedTask
[Fact]
public static void ResultOrCreatedTask_ImplicitConversion_FromResult()
{
CallToolResult callResult = new() { Content = [new TextContentBlock { Text = "hi" }] };
ResultOrCreatedTask<CallToolResult> augmented = callResult;
Assert.False(augmented.IsTask);
Assert.Same(callResult, augmented.Result);
Assert.Null(augmented.TaskCreated);
}
[Fact]
public static void ResultOrCreatedTask_ImplicitConversion_FromCreateTaskResult()
{
CreateTaskResult taskCreated = new()
{
TaskId = "t1",
Status = McpTaskStatus.Working,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
};
ResultOrCreatedTask<CallToolResult> augmented = taskCreated;
Assert.True(augmented.IsTask);
Assert.Same(taskCreated, augmented.TaskCreated);
Assert.Null(augmented.Result);
}
[Fact]
public static void ResultOrCreatedTask_IsTask_FalseForResult_TrueForTask()
{
var result = new ResultOrCreatedTask<CallToolResult>(new CallToolResult());
var task = new ResultOrCreatedTask<CallToolResult>(new CreateTaskResult
{
TaskId = "t",
Status = McpTaskStatus.Working,
CreatedAt = DateTimeOffset.UtcNow,
LastUpdatedAt = DateTimeOffset.UtcNow,
});
Assert.False(result.IsTask);
Assert.True(task.IsTask);
}
#endregion
#region UpdateTaskResult / CancelTaskResult Wire Format
[Fact]
public static void UpdateTaskResult_WireResultType_IsComplete_WhenSet()
{
// SEP-2663: tasks/update responses use resultType="complete".
var result = new UpdateTaskResult { ResultType = "complete" };
string json = JsonSerializer.Serialize(result, McpJsonUtilities.DefaultOptions);
var node = JsonNode.Parse(json)!;
Assert.Equal("complete", (string?)node["resultType"]);
}
[Fact]
public static void CancelTaskResult_WireResultType_IsComplete_WhenSet()
{
// SEP-2663: tasks/cancel responses use resultType="complete".
var result = new CancelTaskResult { ResultType = "complete" };
string json = JsonSerializer.Serialize(result, McpJsonUtilities.DefaultOptions);
var node = JsonNode.Parse(json)!;
Assert.Equal("complete", (string?)node["resultType"]);
}
#endregion
}