InboundClassifier
Decides which protocol era one inbound HTTP request belongs to.
Evaluated exactly once, at the entry boundary, so a single endpoint can serve both eras. The decision is body-primary:
- A request whose
params._metacarries the reserved protocol-version key claims the per-request envelope mechanism and belongs to the era that revision starts. A malformed claim is a validation error, never a silent fall back to the handshake era. - A request without that claim is handshake-era traffic —
initializeincluded, since the modern era has no handshake to route. - The
MCP-Protocol-Versionheader is a cross-check only. It never upgrades or downgrades a claim, and disagreeing with one is an error in its own right; the check has to live here because a body claiming a handshake revision routes to a leg that has no cross-check of its own. - Notifications carry no claim of their own, so for those the header decides.
GETandDELETEare handshake-era session operations: the modern era is POST-only.- A batch is classified element-wise. The modern era removed batching, so an array holding a modern claim is refused rather than split.
The classifier returns plain values and never throws: an era to route to, or a rejection carrying the error to answer with.
Tags
Table of Contents
Constants
- PROTOCOL_VERSION_HEADER : mixed = 'MCP-Protocol-Version'
Methods
- classify() : EraClassification
- crossCheckVersion() : string|null
- The header-against-body check both eras' entries share.
- header() : string|null
- Case-insensitive header lookup, since PSR-7 preserves the sender's casing.
- classifyBatch() : EraClassification
- classifyMessage() : EraClassification
- eraOf() : EraClassification
- The era a claimed revision belongs to.
- namesModern() : bool
- Only a *known* modern revision counts here. An unrecognised header with nothing in the body to back it up is not evidence of an era — it is a version this endpoint does not serve, and the handshake leg's version middleware is what says so, naming everything the endpoint does serve.
Constants
PROTOCOL_VERSION_HEADER
public
mixed
PROTOCOL_VERSION_HEADER
= 'MCP-Protocol-Version'
Methods
classify()
public
classify(string $httpMethod, string|null $body[, array<string, string> $headers = [] ]) : EraClassification
Parameters
- $httpMethod : string
-
the request's HTTP method
- $body : string|null
-
the request body, already read
- $headers : array<string, string> = []
-
request headers, case-insensitively matched
Return values
EraClassificationcrossCheckVersion()
The header-against-body check both eras' entries share.
public
static crossCheckVersion(string|null $headerVersion, string $claimedVersion) : string|null
Kept here rather than in the dispatcher so the edge and the leg it routes to cannot disagree about what a request claims.
Parameters
- $headerVersion : string|null
- $claimedVersion : string
Return values
string|null —the disagreement, or null when the two agree
header()
Case-insensitive header lookup, since PSR-7 preserves the sender's casing.
public
static header(array<string, string> $headers, string $name) : string|null
Parameters
- $headers : array<string, string>
- $name : string
Return values
string|nullclassifyBatch()
private
classifyBatch(array<int, mixed> $messages, string|null $headerVersion) : EraClassification
Parameters
- $messages : array<int, mixed>
- $headerVersion : string|null
Return values
EraClassificationclassifyMessage()
private
classifyMessage(array<string, mixed> $message, string|null $headerVersion) : EraClassification
Parameters
- $message : array<string, mixed>
- $headerVersion : string|null
Return values
EraClassificationeraOf()
The era a claimed revision belongs to.
private
static eraOf(string $version) : EraClassification
An unknown revision counts as modern: it cannot be negotiated through a handshake, and the modern leg is the one that can name what it does serve.
Parameters
- $version : string
Return values
EraClassificationnamesModern()
Only a *known* modern revision counts here. An unrecognised header with nothing in the body to back it up is not evidence of an era — it is a version this endpoint does not serve, and the handshake leg's version middleware is what says so, naming everything the endpoint does serve.
private
static namesModern(string|null $version) : bool
Parameters
- $version : string|null