Add initial task extension conformance coverage (#1734)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7c99928c-a943-49f4-9a63-8b04a0e4d18b
This commit is contained in:
Pranav Senthilnathan 2026-07-24 17:16:20 -07:00 • committed by GitHub
commit 83523b0a42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Http.Json;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.AspNetCore.Tests.Utils;
using ModelContextProtocol.Client;
using ModelContextProtocol.Extensions.Tasks;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
@ -11,6 +12,7 @@ using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization.Metadata;
namespace ModelContextProtocol.AspNetCore.Tests;
@ -606,6 +608,31 @@ public class StreamableHttpClientConformanceTests(ITestOutputHelper outputHelper
Assert.Equal("us-west-2", capturedHeaders["Mcp-Param-Region"]);
}
[Fact]
public async Task TasksClient_SendsRoutingNameHeader_EndToEnd()
{
var capturedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
await StartHeaderCapturingServer(capturedHeaders, supportsTasks: true);
await using var transport = new HttpClientTransport(new()
{
Endpoint = new("http://localhost:5000/mcp"),
TransportMode = HttpTransportMode.StreamableHttp,
}, HttpClient, LoggerFactory);
await using var client = await McpClient.CreateAsync(
transport,
new McpClientOptions { ProtocolVersion = "2026-07-28" },
loggerFactory: LoggerFactory,
cancellationToken: TestContext.Current.CancellationToken);
capturedHeaders.Clear();
await client.GetTaskAsync("task-42", TestContext.Current.CancellationToken);
Assert.Equal("tasks/get", capturedHeaders[McpHttpHeaders.Method]);
Assert.Equal("task-42", capturedHeaders[McpHttpHeaders.Name]);
}
private async Task StartHeaderToolServer()
{
Builder.Services.Configure<JsonOptions>(options =>
@ -683,7 +710,9 @@ public class StreamableHttpClientConformanceTests(ITestOutputHelper outputHelper
await _app.StartAsync(TestContext.Current.CancellationToken);
}
private async Task StartHeaderCapturingServer(Dictionary<string, string> capturedHeaders)
private async Task StartHeaderCapturingServer(
Dictionary<string, string> capturedHeaders,
bool supportsTasks = false)
{
Builder.Services.Configure<JsonOptions>(options =>
{
@ -706,12 +735,45 @@ public class StreamableHttpClientConformanceTests(ITestOutputHelper outputHelper
Result = JsonSerializer.SerializeToNode(new InitializeResult
{
ProtocolVersion = "2025-11-25",
Capabilities = new() { Tools = new() },
Capabilities = new()
{
Tools = new(),
Extensions = supportsTasks
? new Dictionary<string, object>
{
["io.modelcontextprotocol/tasks"] = new JsonObject(),
}
: null,
},
ServerInfo = new Implementation { Name = "header-capture", Version = "1.0" },
}, McpJsonUtilities.DefaultOptions)
});
}
if (request.Method == "server/discover" && supportsTasks)
{
return Results.Json(new JsonRpcResponse
{
Id = request.Id,
Result = JsonSerializer.SerializeToNode(new DiscoverResult
{
SupportedVersions = ["2026-07-28"],
Capabilities = new()
{
Tools = new(),
Extensions = new Dictionary<string, object>
{
["io.modelcontextprotocol/tasks"] = new JsonObject(),
},
},
ServerInfo = new Implementation { Name = "header-capture", Version = "1.0" },
TimeToLive = TimeSpan.Zero,
CacheScope = CacheScope.Private,
ResultType = "complete",
}, McpJsonUtilities.DefaultOptions),
});
}
if (request.Method == "tools/list")
{
return Results.Json(new JsonRpcResponse
@ -732,7 +794,7 @@ public class StreamableHttpClientConformanceTests(ITestOutputHelper outputHelper
});
}
if (request.Method == "tools/call")
if (request.Method is "tools/call" or "tasks/get")
{
// Capture all MCP headers for verification
foreach (var header in context.Request.Headers)
@ -743,6 +805,16 @@ public class StreamableHttpClientConformanceTests(ITestOutputHelper outputHelper
}
}
if (request.Method == "tasks/get")
{
return Results.Json(new JsonRpcResponse
{
Id = request.Id,
Result = JsonNode.Parse(
"""{"taskId":"task-42","status":"working","createdAt":"2026-01-01T00:00:00Z","lastUpdatedAt":"2026-01-01T00:00:00Z"}"""),
});
}
var parameters = JsonSerializer.Deserialize(request.Params, GetJsonTypeInfo<CallToolRequestParams>());
return Results.Json(new JsonRpcResponse
{