Align x-mcp-header implementation with SEP-2243 spec clarifications (#1619)
Co-authored-by: Tarek Mahmoud Sayed <tarekms@ntdev.microsoft.com>
This commit is contained in:
parent
719eade5e0
commit
ed19286a08
2 changed files with 1035 additions and 116 deletions
|
|
@ -630,8 +630,8 @@ internal sealed partial class AIFunctionMcpServerTool : McpServerTool
|
|||
if (!IsPrimitiveHeaderType(paramType))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Parameter '{param.Name}' on method '{method.Name}' has [McpHeader] but is not a primitive type. " +
|
||||
"Only string, numeric, and boolean types may be annotated with [McpHeader].");
|
||||
$"Parameter '{param.Name}' on method '{method.Name}' has [McpHeader] but is not a supported type. " +
|
||||
"Only string, integer, and boolean types may be annotated with [McpHeader].");
|
||||
}
|
||||
|
||||
// Validate case-insensitive uniqueness
|
||||
|
|
@ -673,6 +673,12 @@ internal sealed partial class AIFunctionMcpServerTool : McpServerTool
|
|||
|
||||
private static bool IsPrimitiveHeaderType(Type type)
|
||||
{
|
||||
// Per SEP-2243, x-mcp-header may only be applied to integer, string, or boolean parameters,
|
||||
// and integer values must stay within the JavaScript safe integer range (-2^53+1 to 2^53-1).
|
||||
// ulong is excluded because its upper range (above long.MaxValue) cannot be represented as a
|
||||
// signed integer and the bulk of its domain falls outside the safe range. Remaining integer
|
||||
// types are allowed here; long values are additionally range-checked per value when emitted
|
||||
// (client) and validated (server).
|
||||
return type == typeof(string) ||
|
||||
type == typeof(bool) ||
|
||||
type == typeof(byte) ||
|
||||
|
|
@ -681,10 +687,6 @@ internal sealed partial class AIFunctionMcpServerTool : McpServerTool
|
|||
type == typeof(ushort) ||
|
||||
type == typeof(int) ||
|
||||
type == typeof(uint) ||
|
||||
type == typeof(long) ||
|
||||
type == typeof(ulong) ||
|
||||
type == typeof(float) ||
|
||||
type == typeof(double) ||
|
||||
type == typeof(decimal);
|
||||
type == typeof(long);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using ModelContextProtocol.Client;
|
||||
|
||||
namespace ModelContextProtocol.Server;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -10,8 +12,8 @@ namespace ModelContextProtocol.Server;
|
|||
/// HTTP header named <c>Mcp-Param-{Name}</c>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Only parameters with primitive types (string, number, boolean) may use this attribute.
|
||||
/// The header name must contain only ASCII characters (0x21-0x7E, excluding space and colon)
|
||||
/// Only parameters with primitive types (integer, string, boolean) may use this attribute.
|
||||
/// The header name must match HTTP field-name token syntax (tchar per RFC 9110 Section 5.6.2)
|
||||
/// and must be case-insensitively unique within the tool's input schema.
|
||||
/// </para>
|
||||
/// <para>
|
||||
|
|
@ -38,7 +40,7 @@ public sealed class McpHeaderAttribute : Attribute
|
|||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The name portion of the header. The full header name will be <c>Mcp-Param-{name}</c>.
|
||||
/// Must contain only ASCII characters (0x21-0x7E, excluding space and colon).
|
||||
/// Must match HTTP field-name token syntax (tchar per RFC 9110 Section 5.6.2).
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// The name is null, empty, or contains invalid characters.
|
||||
|
|
@ -59,23 +61,20 @@ public sealed class McpHeaderAttribute : Attribute
|
|||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a header name contains only valid characters.
|
||||
/// Validates that a header name contains only valid HTTP token characters (tchar) per RFC 9110 Section 5.6.2.
|
||||
/// </summary>
|
||||
/// <param name="name">The header name to validate.</param>
|
||||
/// <exception cref="ArgumentException">The name contains invalid characters.</exception>
|
||||
internal static void ValidateHeaderName(string name)
|
||||
{
|
||||
foreach (char c in name)
|
||||
int idx = McpHeaderExtractor.FindFirstNonTchar(name);
|
||||
if (idx >= 0)
|
||||
{
|
||||
// Valid token characters per RFC 9110: visible ASCII (0x21-0x7E) excluding delimiters.
|
||||
// Space (0x20) and colon (':') are explicitly prohibited.
|
||||
if (c < 0x21 || c > 0x7E || c == ':')
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Header name contains invalid character '{c}' (0x{(int)c:X2}). " +
|
||||
"Only ASCII characters (0x21-0x7E) excluding colon are allowed.",
|
||||
nameof(name));
|
||||
}
|
||||
char c = name[idx];
|
||||
throw new ArgumentException(
|
||||
$"Header name contains invalid character '{c}' (0x{(int)c:X2}). " +
|
||||
"Only HTTP token characters (tchar per RFC 9110 Section 5.6.2) are allowed.",
|
||||
nameof(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue