using Microsoft.Extensions.Logging.Abstractions; using System.ComponentModel; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; namespace ModelContextProtocol.Protocol; /// /// Represents an out-of-band notification used to inform the receiver of a progress update for a long-running request. /// /// /// See the schema for more details. /// [JsonConverter(typeof(Converter))] public sealed class ProgressNotificationParams : NotificationParams { /// /// Gets or sets the progress token which was given in the initial request, used to associate this notification with /// the corresponding request. /// /// /// /// This token acts as a correlation identifier that links progress updates to their corresponding request. /// /// /// When an endpoint initiates a request with a in its metadata, /// the receiver can send progress notifications using this same token. This allows both sides to /// correlate the notifications with the original request. /// /// public required ProgressToken ProgressToken { get; set; } /// /// Gets or sets the progress thus far. /// /// /// This should increase for each notification issued as part of the same request, even if the total is unknown. /// public required ProgressNotificationValue Progress { get; set; } /// /// Provides a for . /// [EditorBrowsable(EditorBrowsableState.Never)] public sealed class Converter : JsonConverter { /// public override ProgressNotificationParams? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { ProgressToken? progressToken = null; float? progress = null; float? total = null; string? message = null; JsonObject? meta = null; while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { if (reader.TokenType == JsonTokenType.PropertyName) { var propertyName = reader.GetString(); reader.Read(); switch (propertyName) { case "progressToken": progressToken = (ProgressToken)JsonSerializer.Deserialize(ref reader, options.GetTypeInfo(typeof(ProgressToken)))!; break; case "progress": progress = reader.GetSingle(); break; case "total": total = reader.GetSingle(); break; case "message": message = reader.GetString(); break; case "_meta": meta = JsonSerializer.Deserialize(ref reader, McpJsonUtilities.JsonContext.Default.JsonObject); break; default: reader.Skip(); break; } } } if (progress is null) { throw new JsonException("Missing required property 'progress'."); } if (progressToken is null) { throw new JsonException("Missing required property 'progressToken'."); } return new ProgressNotificationParams { ProgressToken = progressToken.GetValueOrDefault(), Progress = new ProgressNotificationValue { Progress = progress.GetValueOrDefault(), Total = total, Message = message, }, Meta = meta, }; } /// public override void Write(Utf8JsonWriter writer, ProgressNotificationParams value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WritePropertyName("progressToken"); JsonSerializer.Serialize(writer, value.ProgressToken, options.GetTypeInfo(typeof(ProgressToken))); writer.WriteNumber("progress", value.Progress.Progress); if (value.Progress.Total is { } total) { writer.WriteNumber("total", total); } if (value.Progress.Message is { } message) { writer.WriteString("message", message); } if (value.Meta is { } meta) { writer.WritePropertyName("_meta"); JsonSerializer.Serialize(writer, meta, McpJsonUtilities.JsonContext.Default.JsonObject); } writer.WriteEndObject(); } } }