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
4 changed files with 463 additions and 43 deletions

View file

@ -592,7 +592,8 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
headers.Add(McpHttpHeaders.Method, method);
// Add Mcp-Name header for methods that target a specific named resource
string? name = message switch
#pragma warning disable MCPEXP002
string? name = message.Context?.RoutingName ?? message switch
{
JsonRpcRequest { Method: RequestMethods.ToolsCall or RequestMethods.PromptsGet } request
=> GetParamsStringProperty(request.Params, "name"),
@ -600,6 +601,7 @@ internal sealed partial class StreamableHttpClientSessionTransport : TransportBa
=> GetParamsStringProperty(request.Params, "uri"),
_ => null,
};
#pragma warning restore MCPEXP002
if (name is not null)
{

View file

@ -1,4 +1,5 @@
using ModelContextProtocol.Server;
using System.Diagnostics.CodeAnalysis;
using System.Security.Claims;
using System.Text.Json.Serialization;
@ -75,6 +76,17 @@ public sealed class JsonRpcMessageContext
/// </remarks>
public IDictionary<string, object?>? Items { get; set; }
/// <summary>
/// Gets or sets the routing name for this message.
/// </summary>
/// <remarks>
/// Streamable HTTP transports emit this value in the <c>Mcp-Name</c> header. This enables
/// extension methods to identify the named resource targeted by a request.
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
[JsonIgnore]
public string? RoutingName { get; set; }
/// <summary>
/// Gets or sets the protocol version from the transport-level header (e.g. <c>Mcp-Protocol-Version</c>)
/// that accompanied this JSON-RPC message.

View file

@ -1011,6 +1011,12 @@ internal sealed partial class McpServerImpl : McpServer
$"A custom request handler registered through {nameof(McpServerOptions)}.{nameof(McpServerOptions.RequestHandlers)} has a null or empty {nameof(McpServerRequestHandler.Method)}.");
}
if (entry.RoutingNameParameter is not null && string.IsNullOrWhiteSpace(entry.RoutingNameParameter))
{
throw new InvalidOperationException(
$"A custom request handler registered through {nameof(McpServerOptions)}.{nameof(McpServerOptions.RequestHandlers)} has an empty {nameof(McpServerRequestHandler.RoutingNameParameter)}.");
}
// Custom handlers are registered after all built-in handlers, so a method already present
// belongs to a built-in method (e.g. initialize, tools/call) or an earlier custom handler.
// Silently overwriting it would bypass the built-in handler's filters and protocol gating,

View file

@ -24,6 +24,17 @@ public sealed class McpServerRequestHandler
/// </summary>
public required string Method { get; init; }
/// <summary>
/// Gets the name of the top-level request parameter whose value is mirrored in the
/// <c>Mcp-Name</c> HTTP routing header.
/// </summary>
/// <remarks>
/// When set, Streamable HTTP servers require the request to include an <c>Mcp-Name</c>
/// header whose decoded value matches the string value of this parameter.
/// </remarks>
[Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
public string? RoutingNameParameter { get; init; }
/// <summary>
/// Gets the handler function that processes incoming requests for the specified method.
/// </summary>