diff --git a/src/ModelContextProtocol.Core/Authentication/DynamicClientRegistrationResponse.cs b/src/ModelContextProtocol.Core/Authentication/DynamicClientRegistrationResponse.cs index 7d78a698d..ecc2ddc0e 100644 --- a/src/ModelContextProtocol.Core/Authentication/DynamicClientRegistrationResponse.cs +++ b/src/ModelContextProtocol.Core/Authentication/DynamicClientRegistrationResponse.cs @@ -23,7 +23,7 @@ public sealed class DynamicClientRegistrationResponse /// Gets or initializes the redirect URIs for the client. /// [JsonPropertyName("redirect_uris")] - public string[]? RedirectUris { get; init; } + public IList? RedirectUris { get; init; } /// /// Gets or initializes the token endpoint authentication method. @@ -35,13 +35,13 @@ public sealed class DynamicClientRegistrationResponse /// Gets or initializes the grant types that the client will use. /// [JsonPropertyName("grant_types")] - public string[]? GrantTypes { get; init; } + public IList? GrantTypes { get; init; } /// /// Gets or initializes the response types that the client will use. /// [JsonPropertyName("response_types")] - public string[]? ResponseTypes { get; init; } + public IList? ResponseTypes { get; init; } /// /// Gets or initializes the timestamp at which the client ID was issued. diff --git a/src/ModelContextProtocol.Core/Authentication/ProtectedResourceMetadata.cs b/src/ModelContextProtocol.Core/Authentication/ProtectedResourceMetadata.cs index 2e3027da7..51e737f82 100644 --- a/src/ModelContextProtocol.Core/Authentication/ProtectedResourceMetadata.cs +++ b/src/ModelContextProtocol.Core/Authentication/ProtectedResourceMetadata.cs @@ -35,7 +35,7 @@ public sealed class ProtectedResourceMetadata /// OPTIONAL. /// [JsonPropertyName("authorization_servers")] - public List AuthorizationServers { get; set; } = []; + public IList AuthorizationServers { get; set; } = []; /// /// Gets or sets the supported bearer token methods. @@ -48,7 +48,7 @@ public sealed class ProtectedResourceMetadata /// OPTIONAL. /// [JsonPropertyName("bearer_methods_supported")] - public List BearerMethodsSupported { get; set; } = ["header"]; + public IList BearerMethodsSupported { get; set; } = ["header"]; /// /// Gets or sets the supported scopes. @@ -61,7 +61,7 @@ public sealed class ProtectedResourceMetadata /// RECOMMENDED. /// [JsonPropertyName("scopes_supported")] - public List ScopesSupported { get; set; } = []; + public IList ScopesSupported { get; set; } = []; /// /// Gets or sets the URL of the protected resource's JSON Web Key (JWK) Set document. @@ -85,7 +85,7 @@ public sealed class ProtectedResourceMetadata /// OPTIONAL. No default algorithms are implied if this entry is omitted. The value "none" MUST NOT be used. /// [JsonPropertyName("resource_signing_alg_values_supported")] - public List? ResourceSigningAlgValuesSupported { get; set; } + public IList? ResourceSigningAlgValuesSupported { get; set; } /// /// Gets or sets the human-readable name of the protected resource intended for display to the end user. @@ -157,7 +157,7 @@ public sealed class ProtectedResourceMetadata /// OPTIONAL. /// [JsonPropertyName("authorization_details_types_supported")] - public List? AuthorizationDetailsTypesSupported { get; set; } + public IList? AuthorizationDetailsTypesSupported { get; set; } /// /// Gets or sets the list of the JWS algorithm values supported by the resource server for validating DPoP proof JWTs. @@ -170,7 +170,7 @@ public sealed class ProtectedResourceMetadata /// OPTIONAL. /// [JsonPropertyName("dpop_signing_alg_values_supported")] - public List? DpopSigningAlgValuesSupported { get; set; } + public IList? DpopSigningAlgValuesSupported { get; set; } /// /// Gets or sets a value indicating whether the protected resource always requires the use of DPoP-bound access tokens. diff --git a/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs b/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs index ff3d15a58..5fdf85d84 100644 --- a/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs +++ b/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs @@ -693,7 +693,7 @@ public sealed class ToolResultContentBlock : ContentBlock /// audio, resource links, and embedded resources. /// [JsonPropertyName("content")] - public required List Content { get; set; } + public required IList Content { get; set; } /// /// Gets or sets an optional structured result object. diff --git a/src/ModelContextProtocol.Core/Protocol/ListTasksRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/ListTasksRequestParams.cs index 021672380..3036d977b 100644 --- a/src/ModelContextProtocol.Core/Protocol/ListTasksRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/ListTasksRequestParams.cs @@ -30,5 +30,5 @@ public sealed class ListTasksResult : PaginatedResult /// Gets or sets the list of tasks. /// [JsonPropertyName("tasks")] - public required McpTask[] Tasks { get; set; } + public required IList Tasks { get; set; } } diff --git a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs index b1e73c084..16ec7e048 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs @@ -462,7 +462,7 @@ public async ValueTask> ListTasksAsync( var taskResults = await ListTasksAsync(requestParams, cancellationToken).ConfigureAwait(false); if (tasks is null) { - tasks = new List(taskResults.Tasks.Length); + tasks = new List(taskResults.Tasks.Count); } foreach (var mcpTask in taskResults.Tasks) diff --git a/tests/ModelContextProtocol.Tests/Protocol/ListTasksResultTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ListTasksResultTests.cs index 4305d0e81..8d2fbd33b 100644 --- a/tests/ModelContextProtocol.Tests/Protocol/ListTasksResultTests.cs +++ b/tests/ModelContextProtocol.Tests/Protocol/ListTasksResultTests.cs @@ -38,7 +38,7 @@ public static void ListTasksResult_SerializationRoundTrip() // Assert Assert.NotNull(deserialized); Assert.NotNull(deserialized.Tasks); - Assert.Equal(2, deserialized.Tasks.Length); + 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); diff --git a/tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs b/tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs index 56e9aaca9..7d2fc5596 100644 --- a/tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/InMemoryMcpTaskStoreTests.cs @@ -353,7 +353,7 @@ public async Task ListTasksAsync_ReturnsAllTasks() var result = await store.ListTasksAsync(cancellationToken: TestContext.Current.CancellationToken); // Assert - Assert.Equal(2, result.Tasks.Length); + Assert.Equal(2, result.Tasks.Count); Assert.Contains(result.Tasks, t => t.TaskId == task1.TaskId); Assert.Contains(result.Tasks, t => t.TaskId == task2.TaskId); Assert.Null(result.NextCursor); @@ -397,9 +397,9 @@ public async Task ListTasksAsync_SupportsPagination() var secondPageResult = await store.ListTasksAsync(cursor: firstPageResult.NextCursor, cancellationToken: TestContext.Current.CancellationToken); // Assert - Assert.Equal(100, firstPageResult.Tasks.Length); + Assert.Equal(100, firstPageResult.Tasks.Count); Assert.NotNull(firstPageResult.NextCursor); - Assert.Equal(50, secondPageResult.Tasks.Length); + Assert.Equal(50, secondPageResult.Tasks.Count); Assert.Null(secondPageResult.NextCursor); } @@ -779,11 +779,11 @@ public async Task ListTasksAsync_PaginationWithCustomPageSize() var result3 = await store.ListTasksAsync(cursor: result2.NextCursor, cancellationToken: TestContext.Current.CancellationToken); // Assert - Assert.Equal(10, result1.Tasks.Length); + Assert.Equal(10, result1.Tasks.Count); Assert.NotNull(result1.NextCursor); - Assert.Equal(10, result2.Tasks.Length); + Assert.Equal(10, result2.Tasks.Count); Assert.NotNull(result2.NextCursor); - Assert.Equal(5, result3.Tasks.Length); + Assert.Equal(5, result3.Tasks.Count); Assert.Null(result3.NextCursor); // Verify no duplicates across pages @@ -854,7 +854,7 @@ public async Task ListTasksAsync_ConsistentWithExpiredTasksRemovedBetweenPages() var result2 = await store.ListTasksAsync(cursor: result1.NextCursor, cancellationToken: TestContext.Current.CancellationToken); // Assert - First page should have 5 tasks, second page should have 0 (all expired) - Assert.Equal(5, result1.Tasks.Length); + Assert.Equal(5, result1.Tasks.Count); Assert.NotNull(result1.NextCursor); Assert.Empty(result2.Tasks); Assert.Null(result2.NextCursor); @@ -874,7 +874,7 @@ public async Task ListTasksAsync_KeysetPaginationMaintainsConsistencyWithNewTask // Get first page var result1 = await store.ListTasksAsync(cancellationToken: TestContext.Current.CancellationToken); - Assert.Equal(5, result1.Tasks.Length); + Assert.Equal(5, result1.Tasks.Count); // Add more tasks between pages (these should appear in later queries, not retroactively in page 2) for (int i = 10; i < 15; i++) @@ -886,7 +886,7 @@ public async Task ListTasksAsync_KeysetPaginationMaintainsConsistencyWithNewTask var result2 = await store.ListTasksAsync(cursor: result1.NextCursor, cancellationToken: TestContext.Current.CancellationToken); // Assert - Second page should have 5 tasks from original set - Assert.Equal(5, result2.Tasks.Length); + Assert.Equal(5, result2.Tasks.Count); Assert.NotNull(result2.NextCursor); // Verify no overlap between pages @@ -1128,14 +1128,14 @@ public async Task ListTasksAsync_KeysetPaginationWorksWithIdenticalTimestamps() var result1 = await store.ListTasksAsync(cancellationToken: TestContext.Current.CancellationToken); // Assert - First page should have 5 tasks - Assert.Equal(5, result1.Tasks.Length); + Assert.Equal(5, result1.Tasks.Count); Assert.NotNull(result1.NextCursor); // Get second page using cursor var result2 = await store.ListTasksAsync(cursor: result1.NextCursor, cancellationToken: TestContext.Current.CancellationToken); // Assert - Second page should have 5 tasks - Assert.Equal(5, result2.Tasks.Length); + Assert.Equal(5, result2.Tasks.Count); Assert.Null(result2.NextCursor); // No more pages // Verify no overlap between pages @@ -1180,7 +1180,7 @@ public async Task ListTasksAsync_TasksCreatedAfterFirstPageWithSameTimestampAppe // Get first page - should have 5 tasks with a cursor var result1 = await store.ListTasksAsync(cancellationToken: TestContext.Current.CancellationToken); - Assert.Equal(5, result1.Tasks.Length); + Assert.Equal(5, result1.Tasks.Count); Assert.NotNull(result1.NextCursor); // Now create 5 more tasks AFTER we got the first page cursor