Why a remote MCP server answers 405 after the 2026-07-28 revision

A legacy SSE fallback sends GET to a POST-only 2026-07-28 MCP endpoint, so its 405 can hide an earlier connection failure.

A remote MCP server can return HTTP 405 when a client that still uses the HTTP+SSE behavior from protocol version 2024-11-05 falls back to GET against a Streamable HTTP endpoint that supports only POST. In UsefulSoftwareCo/executor issue 2187, that 405 hid an earlier 404 caused by a legacy session reaching a different server runtime.

Key facts

  • On 2026-07-28, MCP removed protocol-level sessions and the Mcp-Session-Id header, and removed the initialize/notifications/initialized handshake. Each request carries the protocol version and client capabilities in _meta.
  • The 2026-07-28 Streamable HTTP transport sends every client JSON-RPC message as HTTP POST. A server that supports only this revision should answer a legacy client's GET or DELETE with HTTP 405.
  • The 2026-07-28 revision replaced the standalone GET notification stream with subscriptions/listen over POST and removed Last-Event-ID stream resumption.
  • Issue 2187, opened on 2026-10-04, records a 404 from the original Streamable HTTP exchange and a 405 from the client's subsequent SSEClientTransport GET.
  • The Python SDK advisory published on 2026-09-30 affects stateful Streamable HTTP sessions in mcp versions 1.8.0 through 1.29.1 and 2.0.0 through 2.1.1. The patched versions are 1.30.0 and 2.2.0.
  • As of 2026-10-06, executor pull request 2189 is open. It proposes @modelcontextprotocol/client and @modelcontextprotocol/core 2.0.0 plus diagnostics that preserve the original and fallback errors.

Why the 405 is expected

The 2026-07-28 Streamable HTTP transport exposes one MCP endpoint that supports POST. Each client request or notification is a new POST. The server returns either one JSON object or an SSE response stream scoped to that request. Long-lived change notifications use a subscriptions/listen request and its POST response stream.

The revision has no protocol session to terminate with DELETE and no standalone GET stream for server-initiated messages. It also does not support resumable SSE streams through Last-Event-ID. The specification therefore defines a compatibility response for a server that supports only 2026-07-28: GET or DELETE to the MCP endpoint receives 405 Method Not Allowed.

That 405 is about the HTTP method. A valid POST can still reach the MCP server. Treating the 405 as the first failure sends debugging in the wrong direction.

How executor issue 2187 reaches 405

Issue 2187 describes a client that uses @modelcontextprotocol/sdk 1.30.0. It sends an initialize request proposing protocol version 2025-11-25. The server supports 2026-07-28 and 2025-06-18, so the client is negotiated onto the older session-based protocol.

The first POST reaches server runtime A. initialize returns 200 and creates a session in that runtime's memory. The client then sends notifications/initialized, but a load-balanced request reaches runtime B. Runtime B has no copy of the session and returns 404.

The client treats connect-phase 404 and 405 as reasons to try SSEClientTransport. That legacy transport sends GET to the same /mcp endpoint. A server implementing only the 2026-07-28 route answers 405, and the fallback error replaces the original 404 in the dashboard.

The issue's trace is:

POST /mcp -> 200 OK  (legacy initialize on runtime A)
POST /mcp -> 404      (notifications/initialized on runtime B)
GET  /mcp -> 405      (SSE fallback against the POST-only route)

The useful diagnostic is the whole sequence, not the last status code. Capture the first POST status and response body before any transport fallback runs.

What to change in clients and servers

Clients should inspect an initial 400, 404, or 405 response before falling back. The 2026-07-28 specification says to recognize a modern JSON-RPC error first. A recognized 2026-07-28 error means the server speaks that protocol, so the client should correct or renegotiate the request instead of sending a legacy GET. Only an unrecognized response should enter the HTTP+SSE compatibility path.

As of 2026-10-06, executor pull request 2189 proposes moving the affected apps to @modelcontextprotocol/client and @modelcontextprotocol/core 2.0.0. It also preserves the primary connection failure beside the sanitized fallback status or timeout. The pull request is open, so it is evidence of a proposed fix, not proof that every released executor client contains it.

The Python advisory is a separate server-side concern. In the affected mcp releases, stateful Streamable HTTP was the default and sessions were not reclaimed reliably. A client DELETE ended a session but left its entry registered, and refused session openings could also remain registered. Upgrade to mcp 1.30.0 or 2.2.0. If an application does not need per-session state, the advisory also documents stateless_http=True for mcp 1.12.0 and later.

The memory-retention bug does not cause every 405. The 405 in issue 2187 comes from the client's legacy GET fallback. The two problems meet when a client still expects session-based behavior and a server has moved to the stateless 2026-07-28 route.

Sources

Last verified: 2026-10-06.

Spotted an outdated or wrong claim? Agents can report it with evidence throughPOST /api/feedback; an editor checks every report. See llms.txt for the agent API.