using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using System.ComponentModel;
namespace ModelContextProtocol.Tests.Server;
///
/// Verifies that the legacy resources/subscribe and resources/unsubscribe RPCs are
/// gated by protocol version. SEP-2575 (the 2026-07-28 revision) removes them in favor of
/// subscriptions/listen with resourceSubscriptions; servers must respond with
/// -32601 MethodNotFound. Initialize-handshake protocol versions still support the legacy
/// RPCs per the spec.
///
public sealed class ResourceSubscriptionProtocolGatingTests : ClientServerTestBase
{
public ResourceSubscriptionProtocolGatingTests(ITestOutputHelper testOutputHelper)
: base(testOutputHelper, startServer: false)
{
}
protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder)
{
mcpServerBuilder.WithResources();
}
[McpServerResourceType]
private sealed class SubscribableResources
{
[McpServerResource(UriTemplate = "test://resource/{id}"), Description("A subscribable test resource")]
public static string GetResource(string id) => $"Resource content: {id}";
}
[Fact]
public async Task Subscribe_OnJuly2026ProtocolSession_ReturnsMethodNotFound()
{
StartServer();
await using var client = await CreateMcpClientForServer(new McpClientOptions
{
ProtocolVersion = McpProtocolVersions.July2026ProtocolVersion,
});
var ex = await Assert.ThrowsAsync(async () =>
await client.SubscribeToResourceAsync(
new SubscribeRequestParams { Uri = "test://resource/1" },
TestContext.Current.CancellationToken));
Assert.Equal(McpErrorCode.MethodNotFound, ex.ErrorCode);
// The rejection must point callers at the SEP-2575 replacement.
Assert.Contains(RequestMethods.SubscriptionsListen, ex.Message, StringComparison.Ordinal);
}
[Fact]
public async Task Unsubscribe_OnJuly2026ProtocolSession_ReturnsMethodNotFound()
{
StartServer();
await using var client = await CreateMcpClientForServer(new McpClientOptions
{
ProtocolVersion = McpProtocolVersions.July2026ProtocolVersion,
});
var ex = await Assert.ThrowsAsync(async () =>
await client.UnsubscribeFromResourceAsync(
new UnsubscribeRequestParams { Uri = "test://resource/1" },
TestContext.Current.CancellationToken));
Assert.Equal(McpErrorCode.MethodNotFound, ex.ErrorCode);
Assert.Contains(RequestMethods.SubscriptionsListen, ex.Message, StringComparison.Ordinal);
}
[Fact]
public async Task Subscribe_OnInitializeHandshakeSession_StillSucceeds()
{
// Default server config; client pinned to 2025-11-25.
StartServer();
await using var client = await CreateMcpClientForServer(new McpClientOptions
{
ProtocolVersion = McpProtocolVersions.November2025ProtocolVersion,
});
// Should complete without throwing on the initialize-handshake revision.
await client.SubscribeToResourceAsync(
new SubscribeRequestParams { Uri = "test://resource/1" },
TestContext.Current.CancellationToken);
await client.UnsubscribeFromResourceAsync(
new UnsubscribeRequestParams { Uri = "test://resource/1" },
TestContext.Current.CancellationToken);
}
}