Follow-up to #873 / PR #1029.
PR #1029 implements the SEP-2260 receive-side SHOULD ("Clients receiving server-to-client requests with no associated outbound request SHOULD respond with a -32602 (Invalid Params) error") via a ServiceRole::enforce_peer_request_association hook: the RoleClient implementation rejects restricted requests (sampling/roots/elicitation; ping exempt) with -32602 when the client has no outbound request in flight, gated on negotiated protocol ≥ 2026-07-28.
What it intentionally leaves out is checking which request a server request belongs to. SEP-2260 defines no wire field for this and you can only tell at the transport layer, by which HTTP response stream a message arrived on. The streamable HTTP client merges the standalone GET stream and POST-response SSE streams into one flat stream before the service layer sees anything, so today:
- A restricted request arriving on the standalone GET stream while an unrelated outbound request is in flight is incorrectly accepted
- We can't verify a request arrived on the SSE stream of the specific originating POST
Work: have the streamable HTTP client transport track which stream each inbound message arrived on (standalone GET vs. a specific POST's SSE stream), so the receive-side check can reject restricted requests that didn't come in on the right stream. Stdio has no stream separation, so the coarse in-flight check remains the best we can do there.
This is not required for 2026-07-28 conformance (the receive-side check is a SHOULD, and the coarse check in #1029 covers the conformance scenarios), so this is not on the 2026-07-28 milestone.
Follow-up to #873 / PR #1029.
PR #1029 implements the SEP-2260 receive-side SHOULD ("Clients receiving server-to-client requests with no associated outbound request SHOULD respond with a
-32602(Invalid Params) error") via aServiceRole::enforce_peer_request_associationhook: theRoleClientimplementation rejects restricted requests (sampling/roots/elicitation;pingexempt) with-32602when the client has no outbound request in flight, gated on negotiated protocol ≥ 2026-07-28.What it intentionally leaves out is checking which request a server request belongs to. SEP-2260 defines no wire field for this and you can only tell at the transport layer, by which HTTP response stream a message arrived on. The streamable HTTP client merges the standalone GET stream and POST-response SSE streams into one flat stream before the service layer sees anything, so today:
Work: have the streamable HTTP client transport track which stream each inbound message arrived on (standalone GET vs. a specific POST's SSE stream), so the receive-side check can reject restricted requests that didn't come in on the right stream. Stdio has no stream separation, so the coarse in-flight check remains the best we can do there.
This is not required for 2026-07-28 conformance (the receive-side check is a SHOULD, and the coarse check in #1029 covers the conformance scenarios), so this is not on the 2026-07-28 milestone.