{
  "schema_version": "1.3.0",
  "slug": "in-what-scenarios-must-an-instance-treat-mcp-streamable-session-resumption-as-unsafe-after-sse-fallback-negotiation",
  "title": "In what scenarios must an instance treat MCP streamable-session resumption as unsafe after SSE fallback negotiation",
  "date_of_record": "2026 November 12",
  "scope": "Applies to MCP servers and clients using the HTTP transport with `Last-Event-ID`-based stream resumption when the client has negotiated down from a streamable HTTP POST response to a legacy SSE GET stream mid-session. Does not apply to pure SSE-only servers, pure streamable-HTTP servers that never fall back, or stdio transports.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-mcp-streamable-session-resumption-as-unsafe-after-sse-fallback-negotiation.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-mcp-streamable-session-resumption-as-unsafe-after-sse-fallback-negotiation.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-mcp-streamable-session-resumption-as-unsafe-after-sse-fallback-negotiation.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1451,
    "python_slice_tokens": 225,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The MCP HTTP transport carries two things that look like the same session but are not: the *streamable session* (a server-side state machine keyed by `Mcp-Session-Id`, resumable via `Last-Event-ID` on a POST response stream) and the *SSE fallback stream* (a long-lived GET event channel that some deployments use when the streamable POST response cannot be hel...",
    "what_was_observed": "In a deployment behind a buffering reverse proxy, a client established a session, issued `initialize`, then `tools/call` over a streamable POST. The proxy buffered the POST response, so the server's streamable response stream stalled past the client's read timeout. The client fell back to `GET /mcp` with `Accept: text/event-stream`, received a fresh SSE stream, and continued. Later, a transient network drop occurred....",
    "condition_of_validity": "Resumption via `Last-Event-ID` is valid only when all of the following hold: 1. The event ID was minted by the same server-side event sequence that will service the resume request. 2. The transport mode (streamable POST response vs. SSE GET) is unchanged between the last observed event and the resume. 3. The server's event ID space is not reset by fallback n...",
    "decision_boundary": "An instance must treat resumption as **unsafe** — and must force a full session re-establishment rather than attempting `Last-Event-ID` replay — when any of these are true: - The client has performed SSE fallback negotiation at any point since the last full `initialize`, and the server does not advertise a transport-mode token in its event IDs or in a `Mcp-S...",
    "operational_effect": "When resumption is treated as unsafe, the client must: (a) abandon the `Last-Event-ID` replay, (b) re-issue `initialize` with the same `Mcp-Session-Id` if the server supports session re-attachment, or mint a new one, (c) re-send any in-flight `tools/call` whose result was not observed, and (d) reconcile locally by request ID rather than by event order. The c..."
  },
  "matched_failure_symptoms": [
    "HTTP_502_503_504_CASCADE"
  ],
  "composition_edges": {
    "primary_domain": "epistemic_provenance_and_federation_routing",
    "upstream_precondition_residuals": [
      "https://corpusfetch.com/c/according-to-what-precedence-hierarchy-must-an-instance-resolve-contradictory-directives-across-system-developer-and-user-tiers.md",
      "https://corpusfetch.com/c/how-should-an-instance-decide-that-a-stored-conclusion-has-gone-stale-without-a-new-observation.md"
    ],
    "downstream_consequence_residuals": [
      "https://corpusfetch.com/c/by-what-checkpoint-and-compaction-boundaries-must-an-instance-externalize-working-state-to-prevent-context-window-drift-and-truncation-amnesia.md",
      "https://corpusfetch.com/c/across-what-additive-schema-and-unknown-field-preservation-boundaries-must-an-instance-evolve-wire-contracts-to-prevent-rolling-deploy-deserialization-crashes.md"
    ],
    "co_occurring_sibling_residuals": [
      "https://corpusfetch.com/c/how-can-an-instance-capture-implicit-user-preferences-without-storing-private-data.md",
      "https://corpusfetch.com/c/how-can-an-instance-compress-long-reasoning-traces-into-binding-conclusions.md"
    ]
  },
  "empirical_verification_warrant": {
    "status": "AST_AND_INVARIANT_VERIFIED",
    "syntax_valid": true,
    "code_sha256": "f6464642f9fecc88d4dc535ab912661fd602eb1af412a1d7748fd09b789c206e",
    "verified_classes": [
      "UnsafeResumptionError",
      "SessionState"
    ],
    "verified_functions": [
      "resume_stream",
      "__init__"
    ],
    "verified_functions_count": 2,
    "defensive_guards_count": 3,
    "raised_exceptions": [
      "UnsafeResumptionError"
    ],
    "sample_fail_closed_invariants": [
      "no prior event observed; cannot resume"
    ],
    "stdlib_dependencies": [],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
