Replace concrete collection types with interfaces in public API surface (#1326)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
This commit is contained in:
Copilot 2026-02-19 19:48:45 -08:00 • committed by GitHub
commit 5c35ea9207
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 25 additions and 25 deletions

View file

@ -23,7 +23,7 @@ public sealed class DynamicClientRegistrationResponse
/// Gets or initializes the redirect URIs for the client.
/// </summary>
[JsonPropertyName("redirect_uris")]
public string[]? RedirectUris { get; init; }
public IList<string>? RedirectUris { get; init; }
/// <summary>
/// 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.
/// </summary>
[JsonPropertyName("grant_types")]
public string[]? GrantTypes { get; init; }
public IList<string>? GrantTypes { get; init; }
/// <summary>
/// Gets or initializes the response types that the client will use.
/// </summary>
[JsonPropertyName("response_types")]
public string[]? ResponseTypes { get; init; }
public IList<string>? ResponseTypes { get; init; }
/// <summary>
/// Gets or initializes the timestamp at which the client ID was issued.

View file

@ -35,7 +35,7 @@ public sealed class ProtectedResourceMetadata
/// OPTIONAL.
/// </remarks>
[JsonPropertyName("authorization_servers")]
public List<string> AuthorizationServers { get; set; } = [];
public IList<string> AuthorizationServers { get; set; } = [];
/// <summary>
/// Gets or sets the supported bearer token methods.
@ -48,7 +48,7 @@ public sealed class ProtectedResourceMetadata
/// OPTIONAL.
/// </remarks>
[JsonPropertyName("bearer_methods_supported")]
public List<string> BearerMethodsSupported { get; set; } = ["header"];
public IList<string> BearerMethodsSupported { get; set; } = ["header"];
/// <summary>
/// Gets or sets the supported scopes.
@ -61,7 +61,7 @@ public sealed class ProtectedResourceMetadata
/// RECOMMENDED.
/// </remarks>
[JsonPropertyName("scopes_supported")]
public List<string> ScopesSupported { get; set; } = [];
public IList<string> ScopesSupported { get; set; } = [];
/// <summary>
/// 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.
/// </remarks>
[JsonPropertyName("resource_signing_alg_values_supported")]
public List<string>? ResourceSigningAlgValuesSupported { get; set; }
public IList<string>? ResourceSigningAlgValuesSupported { get; set; }
/// <summary>
/// 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.
/// </remarks>
[JsonPropertyName("authorization_details_types_supported")]
public List<string>? AuthorizationDetailsTypesSupported { get; set; }
public IList<string>? AuthorizationDetailsTypesSupported { get; set; }
/// <summary>
/// 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.
/// </remarks>
[JsonPropertyName("dpop_signing_alg_values_supported")]
public List<string>? DpopSigningAlgValuesSupported { get; set; }
public IList<string>? DpopSigningAlgValuesSupported { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the protected resource always requires the use of DPoP-bound access tokens.

View file

@ -753,7 +753,7 @@ public sealed class ToolResultContentBlock : ContentBlock
/// audio, resource links, and embedded resources.
/// </remarks>
[JsonPropertyName("content")]
public required List<ContentBlock> Content { get; set; }
public required IList<ContentBlock> Content { get; set; }
/// <summary>
/// Gets or sets an optional structured result object.

View file

@ -30,5 +30,5 @@ public sealed class ListTasksResult : PaginatedResult
/// Gets or sets the list of tasks.
/// </summary>
[JsonPropertyName("tasks")]
public required McpTask[] Tasks { get; set; }
public required IList<McpTask> Tasks { get; set; }
}

View file

@ -462,7 +462,7 @@ public abstract partial class McpServer : McpSession
var taskResults = await ListTasksAsync(requestParams, cancellationToken).ConfigureAwait(false);
if (tasks is null)
{
tasks = new List<McpTask>(taskResults.Tasks.Length);
tasks = new List<McpTask>(taskResults.Tasks.Count);
}
foreach (var mcpTask in taskResults.Tasks)

View file

@ -38,7 +38,7 @@ public static class ListTasksResultTests
// 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);

View file

@ -353,7 +353,7 @@ public class InMemoryMcpTaskStoreTests : LoggedTest
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 class InMemoryMcpTaskStoreTests : LoggedTest
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 class InMemoryMcpTaskStoreTests : LoggedTest
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 class InMemoryMcpTaskStoreTests : LoggedTest
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 class InMemoryMcpTaskStoreTests : LoggedTest
// 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 class InMemoryMcpTaskStoreTests : LoggedTest
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 class InMemoryMcpTaskStoreTests : LoggedTest
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 class InMemoryMcpTaskStoreTests : LoggedTest
// 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