From f15f0fc28bfd9ad05b8b07183bdf336149a9bdc5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 03:56:59 +0000 Subject: [PATCH 1/3] Initial plan From 750d0d1af34791582bb83f259085559b73ff26f6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 04:09:52 +0000 Subject: [PATCH 2/3] Seal public Protocol reference types to prevent external inheritance Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Protocol/ContentBlock.cs | 2 +- .../Protocol/ElicitRequestParams.cs | 61 +++++++++++++++++-- .../Protocol/JsonRpcMessageContext.cs | 2 +- .../Protocol/ResourceContents.cs | 2 +- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs b/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs index 04c98bc84..c85478772 100644 --- a/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs +++ b/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs @@ -69,7 +69,7 @@ private protected ContentBlock() /// Provides a polymorphic converter for the class that doesn't require /// setting explicitly. [EditorBrowsable(EditorBrowsableState.Never)] - public class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// public override ContentBlock? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) diff --git a/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs index 0203edd60..8449a0300 100644 --- a/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs @@ -103,7 +103,7 @@ public string Mode public McpTaskMetadata? Task { get; set; } /// Represents a request schema used in a form mode elicitation request. - public class RequestSchema + public sealed class RequestSchema { /// Gets the type of the schema. /// This value is always "object". @@ -161,7 +161,7 @@ protected private PrimitiveSchemaDefinition() /// Provides a polymorphic converter for the class that doesn't require /// setting explicitly. [EditorBrowsable(EditorBrowsableState.Never)] - public class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// public override PrimitiveSchemaDefinition? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -647,6 +647,25 @@ public override void Write(Utf8JsonWriter writer, PrimitiveSchemaDefinition valu } break; +#pragma warning disable MCP9001 // EnumSchema is deprecated but supported for backward compatibility + case EnumSchema enumSchema: +#pragma warning restore MCP9001 + if (enumSchema.Enum is not null) + { + writer.WritePropertyName("enum"); + JsonSerializer.Serialize(writer, enumSchema.Enum, McpJsonUtilities.JsonContext.Default.IListString); + } + if (enumSchema.EnumNames is not null) + { + writer.WritePropertyName("enumNames"); + JsonSerializer.Serialize(writer, enumSchema.EnumNames, McpJsonUtilities.JsonContext.Default.IListString); + } + if (enumSchema.Default is not null) + { + writer.WriteString("default", enumSchema.Default); + } + break; + default: throw new JsonException($"Unexpected schema type: {value.GetType().Name}"); } @@ -1002,8 +1021,42 @@ public override string Type /// This schema is deprecated in favor of . /// [Obsolete(Obsoletions.LegacyTitledEnumSchema_Message, DiagnosticId = Obsoletions.LegacyTitledEnumSchema_DiagnosticId, UrlFormat = Obsoletions.LegacyTitledEnumSchema_Url)] - public sealed class EnumSchema : LegacyTitledEnumSchema + public sealed class EnumSchema : PrimitiveSchemaDefinition { + /// + [JsonPropertyName("type")] + public override string Type + { + get => "string"; + set + { + if (value is not "string") + { + throw new ArgumentException("Type must be 'string'.", nameof(value)); + } + } + } + + /// Gets or sets the list of allowed string values for the enum. + [JsonPropertyName("enum")] + [field: MaybeNull] + public IList Enum + { + get => field ??= []; + set + { + Throw.IfNull(value); + field = value; + } + } + + /// Gets or sets optional display names corresponding to the enum values. + [JsonPropertyName("enumNames")] + public IList? EnumNames { get; set; } + + /// Gets or sets the default value for the enum. + [JsonPropertyName("default")] + public string? Default { get; set; } } /// @@ -1013,7 +1066,7 @@ public sealed class EnumSchema : LegacyTitledEnumSchema /// This schema is deprecated in favor of . /// [Obsolete(Obsoletions.LegacyTitledEnumSchema_Message, DiagnosticId = Obsoletions.LegacyTitledEnumSchema_DiagnosticId, UrlFormat = Obsoletions.LegacyTitledEnumSchema_Url)] - public class LegacyTitledEnumSchema : PrimitiveSchemaDefinition + public sealed class LegacyTitledEnumSchema : PrimitiveSchemaDefinition { /// [JsonPropertyName("type")] diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageContext.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageContext.cs index 38d798dc4..b9c9a2483 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageContext.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageContext.cs @@ -12,7 +12,7 @@ namespace ModelContextProtocol.Protocol; /// but is not serialized as part of the JSON-RPC payload. This includes transport references, execution context, /// and authenticated user information. /// -public class JsonRpcMessageContext +public sealed class JsonRpcMessageContext { /// /// Gets or sets the transport the was received on or should be sent over. diff --git a/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs b/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs index 9c295a1f8..0948c4232 100644 --- a/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs +++ b/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs @@ -61,7 +61,7 @@ private protected ResourceContents() /// Provides a for . /// [EditorBrowsable(EditorBrowsableState.Never)] - public class Converter : JsonConverter + public sealed class Converter : JsonConverter { /// public override ResourceContents? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) From cc194cd8227d685cd0e685c05eaa79b00825a343 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 04:30:27 +0000 Subject: [PATCH 3/3] Delete EnumSchema type and update references to use LegacyTitledEnumSchema Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Protocol/ElicitRequestParams.cs | 71 +------------------ .../Protocol/EnumSchemaTests.cs | 14 ++-- .../PrimitiveSchemaDefinitionTests.cs | 2 +- 3 files changed, 10 insertions(+), 77 deletions(-) diff --git a/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs index 8449a0300..2e50824e1 100644 --- a/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs @@ -315,11 +315,9 @@ public sealed class Converter : JsonConverter { if (enumNames is not null) { - // EnumSchema is deprecated but supported for backward compatibility. - // Use the EnumSchema class, which is an alias for LegacyTitledEnumSchema, - // to ensure backward compatibility with existing code relying on that type. + // LegacyTitledEnumSchema is deprecated but supported for backward compatibility. #pragma warning disable MCP9001 - psd = new EnumSchema + psd = new LegacyTitledEnumSchema #pragma warning restore MCP9001 { Enum = enumValues, @@ -647,25 +645,6 @@ public override void Write(Utf8JsonWriter writer, PrimitiveSchemaDefinition valu } break; -#pragma warning disable MCP9001 // EnumSchema is deprecated but supported for backward compatibility - case EnumSchema enumSchema: -#pragma warning restore MCP9001 - if (enumSchema.Enum is not null) - { - writer.WritePropertyName("enum"); - JsonSerializer.Serialize(writer, enumSchema.Enum, McpJsonUtilities.JsonContext.Default.IListString); - } - if (enumSchema.EnumNames is not null) - { - writer.WritePropertyName("enumNames"); - JsonSerializer.Serialize(writer, enumSchema.EnumNames, McpJsonUtilities.JsonContext.Default.IListString); - } - if (enumSchema.Default is not null) - { - writer.WriteString("default", enumSchema.Default); - } - break; - default: throw new JsonException($"Unexpected schema type: {value.GetType().Name}"); } @@ -1013,52 +992,6 @@ public override string Type public IList? Default { get; set; } } - /// - /// Represents a legacy schema for an enum type with enumNames. - /// This is a compatibility alias for . - /// - /// - /// This schema is deprecated in favor of . - /// - [Obsolete(Obsoletions.LegacyTitledEnumSchema_Message, DiagnosticId = Obsoletions.LegacyTitledEnumSchema_DiagnosticId, UrlFormat = Obsoletions.LegacyTitledEnumSchema_Url)] - public sealed class EnumSchema : PrimitiveSchemaDefinition - { - /// - [JsonPropertyName("type")] - public override string Type - { - get => "string"; - set - { - if (value is not "string") - { - throw new ArgumentException("Type must be 'string'.", nameof(value)); - } - } - } - - /// Gets or sets the list of allowed string values for the enum. - [JsonPropertyName("enum")] - [field: MaybeNull] - public IList Enum - { - get => field ??= []; - set - { - Throw.IfNull(value); - field = value; - } - } - - /// Gets or sets optional display names corresponding to the enum values. - [JsonPropertyName("enumNames")] - public IList? EnumNames { get; set; } - - /// Gets or sets the default value for the enum. - [JsonPropertyName("default")] - public string? Default { get; set; } - } - /// /// Represents a legacy schema for an enum type with enumNames. /// diff --git a/tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs b/tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs index f0addb9d7..95220d614 100644 --- a/tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs +++ b/tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs @@ -298,7 +298,7 @@ public void LegacyTitledEnumSchema_Serializes_Correctly() // Assert Assert.NotNull(deserialized); - var result = Assert.IsType(deserialized); + var result = Assert.IsType(deserialized); Assert.Equal("string", result.Type); Assert.Equal("Environment", result.Title); Assert.Equal("Deployment environment", result.Description); @@ -309,10 +309,10 @@ public void LegacyTitledEnumSchema_Serializes_Correctly() } [Fact] - public void EnumSchema_Serializes_Correctly() + public void LegacyTitledEnumSchema_Direct_Serializes_Correctly() { // Arrange - var schema = new ElicitRequestParams.EnumSchema + var schema = new ElicitRequestParams.LegacyTitledEnumSchema { Title = "Environment", Description = "Deployment environment", @@ -327,7 +327,7 @@ public void EnumSchema_Serializes_Correctly() // Assert Assert.NotNull(deserialized); - var result = Assert.IsType(deserialized); + var result = Assert.IsType(deserialized); Assert.Equal("string", result.Type); Assert.Equal("Environment", result.Title); Assert.Equal("Deployment environment", result.Description); @@ -338,9 +338,9 @@ public void EnumSchema_Serializes_Correctly() } [Fact] - public void Enum_WithEnumNames_Deserializes_As_EnumSchema() + public void Enum_WithEnumNames_Deserializes_As_LegacyTitledEnumSchema() { - // Arrange - JSON with enumNames should deserialize as (deprecated) EnumSchema + // Arrange - JSON with enumNames should deserialize as (deprecated) LegacyTitledEnumSchema string json = """ { "type": "string", @@ -356,7 +356,7 @@ public void Enum_WithEnumNames_Deserializes_As_EnumSchema() // Assert Assert.NotNull(deserialized); - var result = Assert.IsType(deserialized); + var result = Assert.IsType(deserialized); Assert.Equal("string", result.Type); Assert.Equal("Environment", result.Title); Assert.Equal("Deployment environment", result.Description); diff --git a/tests/ModelContextProtocol.Tests/Protocol/PrimitiveSchemaDefinitionTests.cs b/tests/ModelContextProtocol.Tests/Protocol/PrimitiveSchemaDefinitionTests.cs index fc83ee5ed..d7bea3f5f 100644 --- a/tests/ModelContextProtocol.Tests/Protocol/PrimitiveSchemaDefinitionTests.cs +++ b/tests/ModelContextProtocol.Tests/Protocol/PrimitiveSchemaDefinitionTests.cs @@ -283,7 +283,7 @@ public static void LegacyTitledEnumSchema_UnknownProperties_AreIgnored() json, McpJsonUtilities.DefaultOptions); Assert.NotNull(result); - var enumSchema = Assert.IsType(result); + var enumSchema = Assert.IsType(result); Assert.Equal("string", enumSchema.Type); Assert.Equal(2, enumSchema.Enum.Count); Assert.Contains("option1", enumSchema.Enum);