Optimize MCP parameter-header processing

HA
1d ago
+507-19

Summary

Parameter-header processing currently rebuilds descriptor-name validation structures and repeatedly scans the supplied headers. This PR reduces that work while retaining the existing public API and validation behavior.

Depends on #3031. This is a draft stacked on the benchmark PR. After #3031 merges, this branch should be rebased onto main so the final diff contains only the optimization, regression coverage and comparative measurements.

Changes

Descriptor validation

  • Compare borrowed descriptor names instead of constructing lowercase string copies.
  • Use an allocation-free duplicate scan for lists containing up to eight descriptors.
  • Retain ordered-set validation for larger lists, using borrowed views and case-insensitive comparison.
  • Continue validating descriptors on every call; no public descriptor cache or new lifetime requirements are introduced.

Header lookup

  • Avoid constructing prefixed header-name strings on successful validation paths.
  • For larger requests-more than eight descriptors and sixteen supplied headers—build a sorted index of borrowed mirrored-header views and use binary lookup.
  • Retain the linear scan for smaller requests.
  • Construct full header names when needed for error responses.

Behavior preserved

  • Case-insensitive descriptor and header matching.
  • Per-descriptor validation and error ordering.
  • Rejection of duplicate headers matching a declared parameter.
  • Acceptance of duplicate unused headers as opaque input.
  • Existing UTF-8 and sentinel-decoding behavior.
  • Exact numeric comparison and safe-integer boundaries.

Benchmark results

Measured using the unchanged fixtures from #3031, with three alternating baseline/candidate process runs on the same local Linux runner, GCC 13.3, Release configuration and system allocator.

Values below are medians across process runs of the harness’s per-operation realTime statistic. They are not request-latency percentiles.

WorkloadBaselineCandidateChange
Validate headers - 2 descriptors418.99 ns222.58 ns−46.88%
Validate headers - 64 descriptors69.47 µs24.61 µs−64.58%
Reject headers - 2 descriptors1.43 µs1.24 µs−13.83%
Reject headers - 64 descriptors69.55 µs26.16 µs−62.39%
Generate headers - 2 descriptors496.96 ns396.24 ns−20.27%
Generate headers - 64 descriptors23.46 µs23.94 µs+2.06%

All measured small-input cases improved. Large header generation was approximately 2% slower, so this PR does not claim improvement across every workload.

The complete measurement procedure and limitations are documented in benchmark/MCP-header-results.markdown.

Regression coverage

Adds an indexed-path regression case covering:

  • Mixed-case header names and reversed header order.
  • Unicode values requiring sentinel encoding.
  • Duplicate matching headers versus duplicate unused headers.
  • Incorrect argument types.
  • Exact decimal values at the safe-integer boundary.
  • Integers outside the safe range.
  • Case-insensitive duplicate descriptor names.

Verification

  • Release build passed.
  • MCP unit suite: 1,421 passed, zero failed.
  • Three alternating baseline/candidate benchmark comparisons completed.
  • Repository clang-format checks passed.
  • git diff --check passed.

Limitations

The indexing thresholds are internal heuristics, not descriptor-count limits or a proven optimal crossover.

Measurements cover the existing 2/64-descriptor fixtures with short names. Other cardinalities, longer names, platforms and allocators may behave differently. The full repository test matrix and allocation profiling were not run.

These results describe local helper operations, not end-to-end MCP transport or LLM latency.