Default draft protocol support: sessionless + handshake-less (SEP-2575 + SEP-2567) (#1610)
This commit is contained in:
parent
3368bb7349
commit
6c323e783b
8 changed files with 5704 additions and 712 deletions
|
|
@ -84,6 +84,9 @@ public class CancellationTests : ClientServerTestBase
|
|||
|
||||
var clientOptions = new McpClientOptions
|
||||
{
|
||||
// Pin to a legacy protocol version so the client performs the initialize handshake
|
||||
// (the spec rule under test is "the initialize request MUST NOT be cancelled by clients").
|
||||
ProtocolVersion = "2025-11-25",
|
||||
InitializationTimeout = TimeSpan.FromMilliseconds(500),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
using ModelContextProtocol.Protocol;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ModelContextProtocol.Tests.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Serialization tests for the request/result types introduced by the draft protocol revision (SEP-2575).
|
||||
/// </summary>
|
||||
public static class DiscoverProtocolTests
|
||||
{
|
||||
[Fact]
|
||||
public static void DiscoverRequestParams_SerializationRoundTrip_WithMeta()
|
||||
{
|
||||
var original = new DiscoverRequestParams
|
||||
{
|
||||
Meta = new JsonObject
|
||||
{
|
||||
[MetaKeys.ProtocolVersion] = "2026-07-28",
|
||||
[MetaKeys.ClientInfo] = new JsonObject
|
||||
{
|
||||
["name"] = "test-client",
|
||||
["version"] = "1.0",
|
||||
},
|
||||
[MetaKeys.ClientCapabilities] = new JsonObject(),
|
||||
},
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<DiscoverRequestParams>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.NotNull(deserialized.Meta);
|
||||
Assert.Equal("2026-07-28", (string)deserialized.Meta[MetaKeys.ProtocolVersion]!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DiscoverResult_SerializationRoundTrip_PreservesAllProperties()
|
||||
{
|
||||
var original = new DiscoverResult
|
||||
{
|
||||
SupportedVersions = new List<string> { "2025-11-25", "2026-07-28" },
|
||||
Capabilities = new ServerCapabilities
|
||||
{
|
||||
Tools = new ToolsCapability { ListChanged = true },
|
||||
},
|
||||
ServerInfo = new Implementation { Name = "test-server", Version = "2.0" },
|
||||
Instructions = "Use this server for testing.",
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<DiscoverResult>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.Equal(["2025-11-25", "2026-07-28"], deserialized.SupportedVersions);
|
||||
Assert.NotNull(deserialized.Capabilities.Tools);
|
||||
Assert.True(deserialized.Capabilities.Tools.ListChanged);
|
||||
Assert.Equal("test-server", deserialized.ServerInfo.Name);
|
||||
Assert.Equal("Use this server for testing.", deserialized.Instructions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DiscoverResult_SerializationRoundTrip_WithMinimalProperties()
|
||||
{
|
||||
var original = new DiscoverResult
|
||||
{
|
||||
SupportedVersions = new List<string> { "2026-07-28" },
|
||||
Capabilities = new ServerCapabilities(),
|
||||
ServerInfo = new Implementation { Name = "minimal-server", Version = "1.0" },
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<DiscoverResult>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.Single(deserialized.SupportedVersions);
|
||||
Assert.Equal("2026-07-28", deserialized.SupportedVersions[0]);
|
||||
Assert.Null(deserialized.Instructions);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
using ModelContextProtocol.Protocol;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ModelContextProtocol.Tests.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Targeted tests for the SEP-2549 caching hints (<c>ttlMs</c> and <c>cacheScope</c>) on
|
||||
/// <see cref="DiscoverResult"/>. Spec PR #2855 promotes both fields to required on the discover
|
||||
/// response. <see cref="DiscoverResult"/> has <c>required</c> CLR properties for
|
||||
/// <see cref="DiscoverResult.SupportedVersions"/>, <see cref="DiscoverResult.Capabilities"/>, and
|
||||
/// <see cref="DiscoverResult.ServerInfo"/>, which prevents reuse of the parameterized
|
||||
/// <see cref="CacheableResultTests"/> helper (it instantiates via reflection). This file covers the
|
||||
/// same property-shape assertions for <see cref="DiscoverResult"/>.
|
||||
/// </summary>
|
||||
public static class DiscoverResultCacheableTests
|
||||
{
|
||||
private static DiscoverResult NewDiscoverResult() => new()
|
||||
{
|
||||
SupportedVersions = ["2025-11-25", McpSession.DraftProtocolVersion],
|
||||
Capabilities = new ServerCapabilities(),
|
||||
ServerInfo = new Implementation { Name = "test-server", Version = "1.0" },
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public static void DiscoverResult_SerializesTtlMsAsIntegerMilliseconds()
|
||||
{
|
||||
var result = NewDiscoverResult();
|
||||
result.TimeToLive = TimeSpan.FromMilliseconds(300_000);
|
||||
result.CacheScope = CacheScope.Public;
|
||||
|
||||
string json = JsonSerializer.Serialize(result, McpJsonUtilities.DefaultOptions);
|
||||
var node = JsonNode.Parse(json)!.AsObject();
|
||||
|
||||
Assert.True(node.ContainsKey("ttlMs"));
|
||||
Assert.Equal(JsonValueKind.Number, node["ttlMs"]!.GetValueKind());
|
||||
Assert.Equal(300_000, node["ttlMs"]!.GetValue<long>());
|
||||
Assert.Equal("public", node["cacheScope"]!.GetValue<string>());
|
||||
|
||||
var deserialized = JsonSerializer.Deserialize<DiscoverResult>(json, McpJsonUtilities.DefaultOptions)!;
|
||||
Assert.Equal(TimeSpan.FromMilliseconds(300_000), deserialized.TimeToLive);
|
||||
Assert.Equal(CacheScope.Public, deserialized.CacheScope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DiscoverResult_PrivateScope_RoundTrips()
|
||||
{
|
||||
var result = NewDiscoverResult();
|
||||
result.TimeToLive = TimeSpan.Zero;
|
||||
result.CacheScope = CacheScope.Private;
|
||||
|
||||
string json = JsonSerializer.Serialize(result, McpJsonUtilities.DefaultOptions);
|
||||
var node = JsonNode.Parse(json)!.AsObject();
|
||||
|
||||
Assert.True(node.ContainsKey("ttlMs"));
|
||||
Assert.Equal(0, node["ttlMs"]!.GetValue<long>());
|
||||
Assert.Equal("private", node["cacheScope"]!.GetValue<string>());
|
||||
|
||||
var deserialized = JsonSerializer.Deserialize<DiscoverResult>(json, McpJsonUtilities.DefaultOptions)!;
|
||||
Assert.Equal(TimeSpan.Zero, deserialized.TimeToLive);
|
||||
Assert.Equal(CacheScope.Private, deserialized.CacheScope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DiscoverResult_OmitsCachingHints_WhenUnset()
|
||||
{
|
||||
var result = NewDiscoverResult();
|
||||
|
||||
string json = JsonSerializer.Serialize(result, McpJsonUtilities.DefaultOptions);
|
||||
var node = JsonNode.Parse(json)!.AsObject();
|
||||
|
||||
// Backward compatibility: servers that do not set the hints must not emit them.
|
||||
Assert.False(node.ContainsKey("ttlMs"));
|
||||
Assert.False(node.ContainsKey("cacheScope"));
|
||||
|
||||
var deserialized = JsonSerializer.Deserialize<DiscoverResult>(json, McpJsonUtilities.DefaultOptions)!;
|
||||
Assert.Null(deserialized.TimeToLive);
|
||||
Assert.Null(deserialized.CacheScope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DiscoverResult_DeserializesMissingHints_AsNull()
|
||||
{
|
||||
// A response from a pre-PR-#2855 server may omit both fields. Deserialization must succeed
|
||||
// and surface them as null so callers can apply their own defaults.
|
||||
string json =
|
||||
"""
|
||||
{
|
||||
"supportedVersions": ["2025-11-25"],
|
||||
"capabilities": {},
|
||||
"serverInfo": {"name": "x", "version": "1"}
|
||||
}
|
||||
""";
|
||||
|
||||
var deserialized = JsonSerializer.Deserialize<DiscoverResult>(json, McpJsonUtilities.DefaultOptions)!;
|
||||
Assert.Null(deserialized.TimeToLive);
|
||||
Assert.Null(deserialized.CacheScope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DiscoverResult_DeserializesUnknownCacheScope_AsNull()
|
||||
{
|
||||
// A future or unknown cacheScope string must not break deserialization of the entire result.
|
||||
string json =
|
||||
"""
|
||||
{
|
||||
"supportedVersions": ["2025-11-25"],
|
||||
"capabilities": {},
|
||||
"serverInfo": {"name": "x", "version": "1"},
|
||||
"cacheScope": "shared"
|
||||
}
|
||||
""";
|
||||
|
||||
var deserialized = JsonSerializer.Deserialize<DiscoverResult>(json, McpJsonUtilities.DefaultOptions)!;
|
||||
Assert.Null(deserialized.CacheScope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DiscoverResult_ImplementsICacheableResult()
|
||||
{
|
||||
// Compile-time assertion that DiscoverResult participates in the shared cacheability surface
|
||||
// alongside the list/read result types.
|
||||
Assert.IsAssignableFrom<ICacheableResult>(NewDiscoverResult());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
using ModelContextProtocol.Protocol;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace ModelContextProtocol.Tests.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Serialization tests for the error data payloads introduced by the draft protocol revision (SEP-2575).
|
||||
/// </summary>
|
||||
public static class DraftErrorDataTests
|
||||
{
|
||||
[Fact]
|
||||
public static void UnsupportedProtocolVersionErrorData_SerializationRoundTrip_PreservesAllProperties()
|
||||
{
|
||||
var original = new UnsupportedProtocolVersionErrorData
|
||||
{
|
||||
Supported = new List<string> { "2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25" },
|
||||
Requested = "2026-07-28",
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<UnsupportedProtocolVersionErrorData>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.Equal(4, deserialized.Supported.Count);
|
||||
Assert.Contains("2025-11-25", deserialized.Supported);
|
||||
Assert.Equal("2026-07-28", deserialized.Requested);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void MissingRequiredClientCapabilityErrorData_SerializationRoundTrip_PreservesAllProperties()
|
||||
{
|
||||
var original = new MissingRequiredClientCapabilityErrorData
|
||||
{
|
||||
RequiredCapabilities = new ClientCapabilities
|
||||
{
|
||||
Sampling = new SamplingCapability(),
|
||||
},
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<MissingRequiredClientCapabilityErrorData>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.NotNull(deserialized.RequiredCapabilities.Sampling);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void UnsupportedProtocolVersionException_ExposesRequestedAndSupported()
|
||||
{
|
||||
var ex = new UnsupportedProtocolVersionException("2099-12-31", ["2025-11-25", "2025-06-18"]);
|
||||
|
||||
Assert.Equal(McpErrorCode.UnsupportedProtocolVersion, ex.ErrorCode);
|
||||
Assert.Equal("2099-12-31", ex.Requested);
|
||||
Assert.Equal(2, ex.Supported.Count);
|
||||
Assert.Contains("2025-11-25", ex.Supported);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void MissingRequiredClientCapabilityException_ExposesRequiredCapabilities()
|
||||
{
|
||||
var caps = new ClientCapabilities { Roots = new RootsCapability() };
|
||||
var ex = new MissingRequiredClientCapabilityException(caps);
|
||||
|
||||
Assert.Equal(McpErrorCode.MissingRequiredClientCapability, ex.ErrorCode);
|
||||
Assert.Same(caps, ex.RequiredCapabilities);
|
||||
}
|
||||
}
|
||||
|
|
@ -287,6 +287,8 @@ public partial class ElicitationTypedTests : ClientServerTestBase
|
|||
|
||||
var ex = await Assert.ThrowsAsync<McpProtocolException>(async () =>
|
||||
await client.CallToolAsync("TestElicitationNullablePropertyForm", cancellationToken: TestContext.Current.CancellationToken));
|
||||
|
||||
Assert.Contains("Nullable", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -340,7 +342,7 @@ public partial class ElicitationTypedTests : ClientServerTestBase
|
|||
public sealed class NullablePropertyForm
|
||||
{
|
||||
public string? FirstName { get; set; }
|
||||
public int ZipCode { get; set; }
|
||||
public int? ZipCode { get; set; }
|
||||
public bool IsAdmin { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -760,4 +760,71 @@ public static class JsonRpcMessageConverterTests
|
|||
var error = (JsonRpcError)message;
|
||||
Assert.NotNull(error.Error.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Deserialize_ErrorWithNullId_IsValid()
|
||||
{
|
||||
// Per JSON-RPC 2.0 §5.1, when an error occurs before the request id can be determined
|
||||
// (parse error or invalid request), the server MUST respond with id=null. This shape is
|
||||
// produced by some peers (e.g. Python's simple-streamablehttp-stateless on a draft probe)
|
||||
// and must be accepted so the HTTP-fallback path can recognize the structured signal.
|
||||
string json = """{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"Bad Request"}}""";
|
||||
|
||||
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(message);
|
||||
var error = Assert.IsType<JsonRpcError>(message);
|
||||
Assert.Equal(default(RequestId), error.Id);
|
||||
Assert.Equal(-32600, error.Error.Code);
|
||||
Assert.Equal("Bad Request", error.Error.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Deserialize_ErrorWithMissingId_IsValid()
|
||||
{
|
||||
// Some peers omit `id` entirely on pre-routing errors; treat as null per JSON-RPC 2.0 §5.1.
|
||||
string json = """{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"}}""";
|
||||
|
||||
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(message);
|
||||
var error = Assert.IsType<JsonRpcError>(message);
|
||||
Assert.Equal(default(RequestId), error.Id);
|
||||
Assert.Equal(-32700, error.Error.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Deserialize_RequestWithExplicitNullId_Throws()
|
||||
{
|
||||
// A message carrying a `method` and an explicit `id:null` is a malformed request. Per the MCP
|
||||
// base protocol the request id "MUST NOT be null", and a null id does NOT denote a notification
|
||||
// (JSON-RPC 2.0 notifications omit the id member entirely). The converter must reject it rather
|
||||
// than silently downgrading to a notification (which would swallow the id and skip the response).
|
||||
string json = """{"jsonrpc":"2.0","id":null,"method":"tools/list"}""";
|
||||
|
||||
Assert.Throws<JsonException>(() =>
|
||||
JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Deserialize_RequestWithExplicitNullIdAndParams_Throws()
|
||||
{
|
||||
string json = """{"jsonrpc":"2.0","id":null,"method":"tools/call","params":{"name":"echo"}}""";
|
||||
|
||||
Assert.Throws<JsonException>(() =>
|
||||
JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void Deserialize_NotificationWithoutIdMember_IsNotConfusedWithNullIdRequest()
|
||||
{
|
||||
// Contrast with the explicit-null-id case above: omitting the id member entirely is a valid
|
||||
// notification and must continue to deserialize as one.
|
||||
string json = """{"jsonrpc":"2.0","method":"notifications/cancelled","params":{"requestId":1}}""";
|
||||
|
||||
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
var notification = Assert.IsType<JsonRpcNotification>(message);
|
||||
Assert.Equal("notifications/cancelled", notification.Method);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,4 +35,32 @@ public class RequestIdTests
|
|||
|
||||
Assert.Equal(id, JsonSerializer.Deserialize<RequestId>(JsonSerializer.Serialize(id, McpJsonUtilities.DefaultOptions), McpJsonUtilities.DefaultOptions));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_DeserializesAsDefault()
|
||||
{
|
||||
// Per JSON-RPC 2.0 §5.1, error responses produced before the request id can be determined
|
||||
// MUST carry id=null. Deserialization needs to tolerate that shape so callers can handle
|
||||
// such error envelopes (instead of throwing on the bare RequestId conversion).
|
||||
var id = JsonSerializer.Deserialize<RequestId>("null", McpJsonUtilities.DefaultOptions);
|
||||
Assert.Equal(default(RequestId), id);
|
||||
Assert.Null(id.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_SerializesAsJsonNull()
|
||||
{
|
||||
// The default RequestId (Id == null) is the id-less-error-response shape. It MUST serialize as
|
||||
// JSON null — not "" — so the wire form is spec-conformant and round-trips losslessly.
|
||||
Assert.Equal("null", JsonSerializer.Serialize(default(RequestId), McpJsonUtilities.DefaultOptions));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_Roundtrips()
|
||||
{
|
||||
var json = JsonSerializer.Serialize(default(RequestId), McpJsonUtilities.DefaultOptions);
|
||||
var id = JsonSerializer.Deserialize<RequestId>(json, McpJsonUtilities.DefaultOptions);
|
||||
Assert.Equal(default(RequestId), id);
|
||||
Assert.Null(id.Id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
using ModelContextProtocol.Protocol;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ModelContextProtocol.Tests.Protocol;
|
||||
|
||||
/// <summary>
|
||||
/// Serialization tests for the subscriptions/listen types introduced by the draft protocol revision (SEP-2575).
|
||||
/// </summary>
|
||||
public static class SubscriptionsListenProtocolTests
|
||||
{
|
||||
[Fact]
|
||||
public static void SubscriptionsListenRequestParams_SerializationRoundTrip_PreservesAllProperties()
|
||||
{
|
||||
var original = new SubscriptionsListenRequestParams
|
||||
{
|
||||
Notifications = new SubscriptionsListenNotifications
|
||||
{
|
||||
ToolsListChanged = true,
|
||||
PromptsListChanged = true,
|
||||
ResourcesListChanged = true,
|
||||
ResourceSubscriptions = new List<string> { "file:///foo.txt", "file:///bar.txt" },
|
||||
},
|
||||
Meta = new JsonObject
|
||||
{
|
||||
[MetaKeys.ProtocolVersion] = "2026-07-28",
|
||||
[MetaKeys.LogLevel] = "info",
|
||||
},
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<SubscriptionsListenRequestParams>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.True(deserialized.Notifications.ToolsListChanged);
|
||||
Assert.True(deserialized.Notifications.PromptsListChanged);
|
||||
Assert.True(deserialized.Notifications.ResourcesListChanged);
|
||||
Assert.NotNull(deserialized.Notifications.ResourceSubscriptions);
|
||||
Assert.Equal(["file:///foo.txt", "file:///bar.txt"], deserialized.Notifications.ResourceSubscriptions);
|
||||
Assert.Equal("2026-07-28", (string)deserialized.Meta![MetaKeys.ProtocolVersion]!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void SubscriptionsAcknowledgedNotificationParams_SerializationRoundTrip_PreservesNotifications()
|
||||
{
|
||||
var original = new SubscriptionsAcknowledgedNotificationParams
|
||||
{
|
||||
Notifications = new SubscriptionsListenNotifications
|
||||
{
|
||||
ToolsListChanged = true,
|
||||
},
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(original, McpJsonUtilities.DefaultOptions);
|
||||
var deserialized = JsonSerializer.Deserialize<SubscriptionsAcknowledgedNotificationParams>(json, McpJsonUtilities.DefaultOptions);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.True(deserialized.Notifications.ToolsListChanged);
|
||||
Assert.Null(deserialized.Notifications.PromptsListChanged);
|
||||
Assert.Null(deserialized.Notifications.ResourcesListChanged);
|
||||
Assert.Null(deserialized.Notifications.ResourceSubscriptions);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue