using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
namespace ModelContextProtocol.Tests.Protocol;
///
/// End-to-end tests verifying that SEP-2549 caching hints set by a server on cacheable results
/// are observed by a connected client.
///
public class CacheableResultClientServerTests(ITestOutputHelper testOutputHelper)
: ClientServerTestBase(testOutputHelper)
{
protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder)
{
mcpServerBuilder
.WithListToolsHandler((_, _) => new ValueTask(new ListToolsResult
{
Tools = [new Tool { Name = "echo" }],
TimeToLive = TimeSpan.FromMinutes(5),
CacheScope = CacheScope.Public,
}))
.WithListPromptsHandler((_, _) => new ValueTask(new ListPromptsResult
{
Prompts = [new Prompt { Name = "greet" }],
}))
.WithListResourcesHandler((_, _) => new ValueTask(new ListResourcesResult
{
Resources = [new Resource { Uri = "test://resource", Name = "resource" }],
}))
.WithReadResourceHandler((request, _) => new ValueTask(new ReadResourceResult
{
Contents = [new TextResourceContents { Uri = request.Params!.Uri!, Text = "hi" }],
TimeToLive = TimeSpan.FromSeconds(30),
CacheScope = CacheScope.Private,
}));
}
[Fact]
public async Task ListTools_PropagatesCachingHints_ToClient()
{
await using var client = await CreateMcpClientForServer();
var result = await client.ListToolsAsync(
new ListToolsRequestParams(),
TestContext.Current.CancellationToken);
Assert.Equal(TimeSpan.FromMinutes(5), result.TimeToLive);
Assert.Equal(CacheScope.Public, result.CacheScope);
}
[Fact]
public async Task ReadResource_PropagatesCachingHints_ToClient()
{
await using var client = await CreateMcpClientForServer();
var result = await client.ReadResourceAsync(
"test://resource",
cancellationToken: TestContext.Current.CancellationToken);
Assert.Equal(TimeSpan.FromSeconds(30), result.TimeToLive);
Assert.Equal(CacheScope.Private, result.CacheScope);
}
[Fact]
public async Task ListPrompts_WhenHandlerOmitsHints_ServerInjectsConservativeDefaults()
{
await using var client = await CreateMcpClientForServer();
var result = await client.ListPromptsAsync(
new ListPromptsRequestParams(),
TestContext.Current.CancellationToken);
// SEP-2549: the handler left the hints unset, so the server fills in conservative defaults
// (immediately stale, not shareable) rather than omitting the now-required fields.
Assert.Equal(TimeSpan.Zero, result.TimeToLive);
Assert.Equal(CacheScope.Private, result.CacheScope);
}
[Fact]
public async Task ListResources_WhenHandlerOmitsHints_ServerInjectsConservativeDefaults()
{
await using var client = await CreateMcpClientForServer();
var result = await client.ListResourcesAsync(
new ListResourcesRequestParams(),
TestContext.Current.CancellationToken);
Assert.Equal(TimeSpan.Zero, result.TimeToLive);
Assert.Equal(CacheScope.Private, result.CacheScope);
}
}