Three real defects it caught, plus the tests it asked for:
- The strict `tools/list` aggregate is filtered by the SDK for invalid
`x-mcp-header` annotations (SEP-2243), but the salvage path was not: one
ordinary malformed tool would trip the fallback and readmit every
invalid-header tool beside it. Salvaged tools now go through the same scan.
- `markResponseRejected` resolves "the last response received for this method",
and the re-walk answers that same method — so the mark landed on a salvage
page that succeeded while the actually-invalid response rendered clean. The
correlation id is now captured before the re-walk.
- The report was cleared only on an explicit `disconnect()`. The route out that
tears down nothing (`onerror` with no `onclose`) carried it into the next
connection, so it is now cleared on the start-clean `resetSessionState()` path
as well.
Fixing the first exposed two more, both found by the new test:
- The modern (2026-07-28) codec validates a result against the spec schema
before the caller's, so a lenient `client.request` is rejected exactly as the
strict one was — there was no salvage on the modern leg at all. It now goes
through the raw-wire channel that exists for the modern `tasks/*` methods.
`withModernEnvelope` is split out of `withModernTaskEnvelope` so a list
request doesn't claim the tasks extension the user may have disabled.
- A malformed tool also blanked the SEP-2243 excluded set, leaving a tool that
vanished for an invalid header with no explanation — #1909's own complaint one
level down. The scan walk now tolerates a malformed sibling.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3
Signed-off-by: cliffhall <cliff@futurescale.com>