Wymcp.Plugs.Era (Wymcp v0.6.1)

View Source

Legacy-only

Era classification exists only because a second era does. This module is removed wholesale at the legacy decommission.

Era classification: tags every POST with the era whose lane serves it — conn.assigns[:wymcp_era], :modern or :legacy — and rejects the two era-mix shapes with 400.

Runs after the wire checks, which Wymcp.Router's wire-check invariant names (they are era-invariant, and the singleton-header check guarantees at most one Mcp-Session-Id before this plug reads it), and before Wymcp.Plugs.Session, which passes modern-classified requests through untouched.

The classification table — the modern protocol fields are modern-era evidence; initialize and Mcp-Session-Id are legacy-era evidence:

Signals on the requestClassification
protocol field(s), no session header, not initialize:modern
initialize, no protocol fields:legacy
Mcp-Session-Id, no protocol fields:legacy (session lookup as before)
protocol fields + Mcp-Session-Idreject 400 — era mix
initialize + protocol fieldsreject 400 — era mix
none of the above:legacy (Wymcp.Plugs.Session's missing-header rejection names both eras' next actions)

Either required protocol field's presence classifies modern; Wymcp.Plugs.ProtocolFields downstream enforces that both are present and well-typed. Era-mix fails closed rather than ignoring the legacy artifact: the spec's ignore rule is written for modern-only servers, where no legacy lane exists to mis-route into — at a dual-era server a client sending both signals may believe in session state the modern lane would silently ignore. A request carrying all three signals meets the session-header rejection first (the table's row order is the check order). Headers are never era evidence; only body fields and Mcp-Session-Id presence are read here. That a modern request's headers mirror its body is a separate rule, enforced downstream by Wymcp.Plugs.HeaderBinding once the era is known.