diff --git a/src/ModelContextProtocol.Core/CustomizableJsonStringEnumConverter.cs b/src/ModelContextProtocol.Core/CustomizableJsonStringEnumConverter.cs
index 87bb430df..617b33359 100644
--- a/src/ModelContextProtocol.Core/CustomizableJsonStringEnumConverter.cs
+++ b/src/ModelContextProtocol.Core/CustomizableJsonStringEnumConverter.cs
@@ -5,8 +5,8 @@
using System.Diagnostics.CodeAnalysis;
#if !NET9_0_OR_GREATER
using System.Reflection;
-using System.Text.Json;
#endif
+using System.Text.Json;
using System.Text.Json.Serialization;
#if !NET9_0_OR_GREATER
using ModelContextProtocol;
@@ -66,6 +66,31 @@ public override string ConvertName(string name) =>
}
#endif
}
+
+ ///
+ /// A JSON converter for enums that allows customizing the serialized string value of enum members
+ /// using the .
+ ///
+ ///
+ /// This is a temporary workaround for lack of System.Text.Json's JsonStringEnumConverter<T>
+ /// 9.x support for custom enum member naming. It will be replaced by the built-in functionality
+ /// once .NET 9 is fully adopted.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [RequiresUnreferencedCode("Requires unreferenced code to instantiate the generic enum converter.")]
+ [RequiresDynamicCode("Requires dynamic code to instantiate the generic enum converter.")]
+ public sealed class CustomizableJsonStringEnumConverter : JsonConverterFactory
+ {
+ ///
+ public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum;
+ ///
+ public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
+ {
+ Type converterType = typeof(CustomizableJsonStringEnumConverter<>).MakeGenericType(typeToConvert)!;
+ var factory = (JsonConverterFactory)Activator.CreateInstance(converterType)!;
+ return factory.CreateConverter(typeToConvert, options);
+ }
+ }
}
#if !NET9_0_OR_GREATER
diff --git a/src/ModelContextProtocol.Core/McpJsonUtilities.cs b/src/ModelContextProtocol.Core/McpJsonUtilities.cs
index 0bc9ee777..b075e1263 100644
--- a/src/ModelContextProtocol.Core/McpJsonUtilities.cs
+++ b/src/ModelContextProtocol.Core/McpJsonUtilities.cs
@@ -2,6 +2,7 @@
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
@@ -34,14 +35,22 @@ public static partial class McpJsonUtilities
/// Creates default options to use for MCP-related serialization.
///
/// The configured options.
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
private static JsonSerializerOptions CreateDefaultOptions()
{
// Copy the configuration from the source generated context.
JsonSerializerOptions options = new(JsonContext.Default.Options);
- // Chain with all supported types from MEAI
+ // Chain with all supported types from MEAI.
options.TypeInfoResolverChain.Add(AIJsonUtilities.DefaultOptions.TypeInfoResolver!);
+ // Add a converter for user-defined enums, if reflection is enabled by default.
+ if (JsonSerializer.IsReflectionEnabledByDefault)
+ {
+ options.Converters.Add(new CustomizableJsonStringEnumConverter());
+ }
+
options.MakeReadOnly();
return options;
}
diff --git a/tests/ModelContextProtocol.Tests/McpJsonUtilitiesTests.cs b/tests/ModelContextProtocol.Tests/McpJsonUtilitiesTests.cs
index 18dc680ef..e0af61eed 100644
--- a/tests/ModelContextProtocol.Tests/McpJsonUtilitiesTests.cs
+++ b/tests/ModelContextProtocol.Tests/McpJsonUtilitiesTests.cs
@@ -1,4 +1,6 @@
using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
namespace ModelContextProtocol.Tests;
@@ -22,4 +24,27 @@ public static void DefaultOptions_UseReflectionWhenEnabled()
Assert.Equal(JsonSerializer.IsReflectionEnabledByDefault, options.TryGetTypeInfo(anonType, out _));
}
+
+ [Fact]
+ public static void DefaultOptions_UnknownEnumHandling()
+ {
+ var options = McpJsonUtilities.DefaultOptions;
+
+ if (JsonSerializer.IsReflectionEnabledByDefault)
+ {
+ Assert.Equal("\"A\"", JsonSerializer.Serialize(EnumWithoutAnnotation.A, options));
+ Assert.Equal("\"A\"", JsonSerializer.Serialize(EnumWithAnnotation.A, options));
+ }
+ else
+ {
+ options = new(options) { TypeInfoResolver = new DefaultJsonTypeInfoResolver() };
+ Assert.Equal("1", JsonSerializer.Serialize(EnumWithoutAnnotation.A, options));
+ Assert.Equal("\"A\"", JsonSerializer.Serialize(EnumWithAnnotation.A, options));
+ }
+ }
+
+ public enum EnumWithoutAnnotation { A = 1, B = 2, C = 3 }
+
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum EnumWithAnnotation { A = 1, B = 2, C = 3 }
}