Skip to content

Align x-mcp-header implementation with SEP-2243 spec clarifications#1619

Open
tarekgh wants to merge 1 commit into
modelcontextprotocol:mainfrom
tarekgh:sep-2243-spec-compliance
Open

Align x-mcp-header implementation with SEP-2243 spec clarifications#1619
tarekgh wants to merge 1 commit into
modelcontextprotocol:mainfrom
tarekgh:sep-2243-spec-compliance

Conversation

@tarekgh
Copy link
Copy Markdown
Contributor

@tarekgh tarekgh commented Jun 1, 2026

Summary

Implements the spec clarifications from modelcontextprotocol/modelcontextprotocol#2772 to ensure the SDK fully complies with SEP-2243.

Changes

RFC 9110 tchar validation for header names

  • Replace loose ASCII range check with strict RFC 9110 Section 5.6.2 tchar character set
  • Use SearchValues on .NET 8+ for SIMD-accelerated matching
  • Use bitmap lookup (two ulong values covering ASCII 0-127) on netstandard2.0
  • Share validation logic between McpHeaderAttribute and McpHeaderExtractor via internal FindFirstNonTchar method

Remove "number" from allowed x-mcp-header types

  • Only "string", "integer", and "boolean" are permitted per the updated spec
  • Client-side: McpHeaderExtractor.ValidateToolSchema rejects tools with type "number" on x-mcp-header properties
  • Server-side: IsPrimitiveHeaderType no longer accepts float, double, or decimal for [McpHeader]

Base64 sentinel collision check

  • McpHeaderEncoder.RequiresBase64Encoding now detects values matching the =?base64?...?= pattern and forces base64 encoding to prevent round-trip confusion
  • DecodeValue changed from case-insensitive to case-sensitive prefix matching per spec requirement that sentinel markers must appear exactly as shown (lowercase)

Nested property support

  • Client-side header extraction and validation now recursively traverse nested "properties" objects at any depth, per the spec allowance for x-mcp-header at any nesting depth
  • Server-side StreamableHttpHandler adds recursive ValidateCustomParamHeadersFromProperties for header/body comparison on nested properties

Tests

28 new tests added:

  • 14 tchar validation tests (McpHeaderAttributeTests)
  • 7 sentinel collision tests (McpHeaderEncoderTests)
  • 7 integration tests for number rejection, nested properties, and tchar enforcement (McpHeaderExtractorValidationTests, new file)

All existing tests pass across net8.0, net9.0, net10.0, and net472.

- Enforce RFC 9110 tchar validation for header names using SearchValues on
  .NET 8+ and bitmap lookup on netstandard2.0
- Remove 'number' from allowed x-mcp-header types (only string, integer,
  boolean are permitted); reject float/double/decimal in [McpHeader]
- Add base64 sentinel collision check in McpHeaderEncoder.RequiresBase64Encoding
  so literal values resembling =?base64?...?= are encoded to avoid confusion
- Fix DecodeValue to use case-sensitive prefix matching per spec requirement
  that sentinel markers must appear exactly as shown (lowercase)
- Support nested property traversal for x-mcp-header validation and extraction
  on both client and server sides
- Share tchar validation logic between McpHeaderAttribute and McpHeaderExtractor
  via internal FindFirstNonTchar method
- Add recursive ValidateCustomParamHeadersFromProperties in StreamableHttpHandler
  for server-side nested property header/body comparison
- Add 28 new tests covering tchar validation, sentinel collision encoding,
  number type rejection, nested property handling, and case-sensitive decoding
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the SDK’s SEP-2243 x-mcp-header handling to match the latest spec clarifications, tightening header-name validation, restricting allowed header types, hardening Base64 sentinel parsing, and extending validation/extraction to nested schema properties.

Changes:

  • Enforce RFC 9110 tchar validation for x-mcp-header / [McpHeader] names and share that logic across client/server code.
  • Disallow "number" / floating-point header types for x-mcp-header (client-side schema rejection; server-side attribute/type checks).
  • Make Base64 sentinel decoding case-sensitive and add “sentinel collision” encoding safeguards; add nested-schema traversal for x-mcp-header.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/ModelContextProtocol.Tests/Server/McpHeaderAttributeTests.cs Expands test coverage for RFC 9110 tchar header-name validation.
tests/ModelContextProtocol.Tests/Client/McpHeaderExtractorValidationTests.cs Adds integration tests for number rejection, nested properties, and tchar enforcement.
tests/ModelContextProtocol.Tests/Client/McpHeaderEncoderTests.cs Adds sentinel-collision tests and asserts case-sensitive sentinel decoding.
src/ModelContextProtocol.Core/Server/McpHeaderAttribute.cs Switches header-name validation to RFC 9110 tchar via shared helper.
src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs Tightens [McpHeader] supported types to integer/string/boolean (removes float/double/decimal).
src/ModelContextProtocol.Core/Protocol/McpHeaderEncoder.cs Enforces case-sensitive sentinel parsing and prevents sentinel-collision ambiguity.
src/ModelContextProtocol.Core/Client/McpHeaderExtractor.cs Adds nested property traversal and stricter schema validation for x-mcp-header.
src/ModelContextProtocol.Core/Client/McpClient.Methods.cs Updates inline comments around x-mcp-header validation behavior.
src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs Adds recursive server-side validation for nested x-mcp-header properties.

Comment on lines 189 to +191
// Validate x-mcp-header annotations per SEP-2243.
// Clients MUST exclude tools with invalid annotations and SHOULD log a warning.
// Streamable HTTP clients MUST exclude tools with invalid annotations;
// non-HTTP clients MAY also reject them for safety.
Comment on lines +183 to 187
// MUST only be applied to primitive types (integer, string, boolean).
// Parameters with type "number" are not permitted.
if (property.Value.TryGetProperty("type", out var typeElement) &&
typeElement.ValueKind == JsonValueKind.String)
{
Comment on lines +185 to +194
[Theory]
[InlineData("=?BASE64?literal?=", false)] // Case-sensitive: uppercase prefix does not match sentinel
[InlineData("=?base64?start", false)] // Missing suffix: no sentinel match
[InlineData("end?=", false)] // Missing prefix: no sentinel match
[InlineData("plain-text", false)] // No sentinel pattern
public void EncodeValue_NonSentinelPattern_NotBase64Encoded(string input, bool _)
{
var result = McpHeaderEncoder.EncodeValue(input);
Assert.Equal(input, result);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants