using ModelContextProtocol.Protocol.Messages; using ModelContextProtocol.Protocol.Types; using System.Diagnostics.CodeAnalysis; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; namespace ModelContextProtocol.Utils.Json; /// Provides a collection of utility methods for working with JSON data in the context of MCP. public static partial class McpJsonUtilities { /// /// Gets the singleton used as the default in JSON serialization operations. /// /// /// /// For Native AOT or applications disabling , this instance /// includes source generated contracts for all common exchange types contained in the ModelContextProtocol library. /// /// /// It additionally turns on the following settings: /// /// Enables string-based enum serialization as implemented by . /// Enables as the default ignore condition for properties. /// Enables as the default number handling for number types. /// /// Enables when escaping JSON strings. /// Consuming applications must ensure that JSON outputs are adequately escaped before embedding in other document formats, /// such as HTML and XML. /// /// /// /// public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions(); /// /// Creates default options to use for MCP-related serialization. /// /// The configured options. [UnconditionalSuppressMessage("AotAnalysis", "IL3050", Justification = "DefaultJsonTypeInfoResolver is only used when reflection-based serialization is enabled")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "DefaultJsonTypeInfoResolver is only used when reflection-based serialization is enabled")] private static JsonSerializerOptions CreateDefaultOptions() { // If reflection-based serialization is enabled by default, use it, as it's the most permissive in terms of what it can serialize, // and we want to be flexible in terms of what can be put into the various collections in the object model. // Otherwise, use the source-generated options to enable trimming and Native AOT. JsonSerializerOptions options; if (JsonSerializer.IsReflectionEnabledByDefault) { // Keep in sync with the JsonSourceGenerationOptions attribute on JsonContext below. options = new(JsonSerializerDefaults.Web) { TypeInfoResolver = new DefaultJsonTypeInfoResolver(), Converters = { new JsonStringEnumConverter() }, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, NumberHandling = JsonNumberHandling.AllowReadingFromString, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, }; } else { options = new(JsonContext.Default.Options) { // Compile-time encoder setting not yet available Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, }; } options.MakeReadOnly(); return options; } internal static JsonTypeInfo GetTypeInfo(this JsonSerializerOptions options) => (JsonTypeInfo)options.GetTypeInfo(typeof(T)); internal static JsonElement DefaultMcpToolSchema = ParseJsonElement("{\"type\":\"object\"}"u8); internal static bool IsValidMcpToolSchema(JsonElement element) { if (element.ValueKind is not JsonValueKind.Object) { return false; } foreach (JsonProperty property in element.EnumerateObject()) { if (property.NameEquals("type")) { if (property.Value.ValueKind is not JsonValueKind.String || !property.Value.ValueEquals("object")) { return false; } return true; // No need to check other properties } } return false; // No type keyword found. } // Keep in sync with CreateDefaultOptions above. [JsonSourceGenerationOptions(JsonSerializerDefaults.Web, UseStringEnumConverter = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, NumberHandling = JsonNumberHandling.AllowReadingFromString)] [JsonSerializable(typeof(JsonDocument))] [JsonSerializable(typeof(JsonElement))] [JsonSerializable(typeof(JsonNode))] [JsonSerializable(typeof(IJsonRpcMessage))] [JsonSerializable(typeof(JsonRpcRequest))] [JsonSerializable(typeof(JsonRpcNotification))] [JsonSerializable(typeof(JsonRpcResponse))] [JsonSerializable(typeof(JsonRpcError))] [JsonSerializable(typeof(ServerCapabilities))] [JsonSerializable(typeof(ClientCapabilities))] [JsonSerializable(typeof(Implementation))] [JsonSerializable(typeof(CreateMessageResult))] [JsonSerializable(typeof(ListRootsResult))] [JsonSerializable(typeof(InitializeResult))] [JsonSerializable(typeof(CallToolResponse))] internal sealed partial class JsonContext : JsonSerializerContext; private static JsonElement ParseJsonElement(ReadOnlySpan utf8Json) { Utf8JsonReader reader = new(utf8Json); return JsonElement.ParseValue(ref reader); } }