Describe the bug
Stateful Streamable HTTP session expiry is not correctly reflected in the PHP SDK’s connection state.
In the affected HttpTransport::send() implementation, HTTP status codes are not checked before processing or discarding the response body.
Consequently:
- A session-bound HTTP 404 with an empty or plain-text body leaves the tool request waiting until timeout.
Client::isConnected() remains true, and subsequent requests continue using the expired session.
- A 404 returned for
notifications/cancelled is also discarded without invalidating the connection.
This concerns protocol revisions using Mcp-Session-Id, such as 2025-11-25.
To Reproduce
Ordinary tool request
- Initialize a connection to a stateful HTTP server and receive session ID
S.
- Invalidate
S on the server.
- Call a tool. The server returns HTTP 404 with an empty or plain-text body.
- Observe that the request waits until timeout and the client still reports itself connected.
- Make another call: it sends the same expired session ID.
Cancellation or deadline expiry
- Start a tool call using session
S and leave its response pending.
- Invalidate the session, then cancel the call or let its deadline expire.
- The SDK sends
notifications/cancelled with S; the server returns HTTP 404.
- The original call is interrupted, but the client remains marked connected despite its expired session.
Simply adding a ConnectionException for HTTP 404 is insufficient for the second case: Protocol::notifyCancellation() catches notification failures. Connection invalidation must persist independently of whether that exception reaches the caller.
Expected behavior
- Recognize a 404 on a request carrying
Mcp-Session-Id as session expiry.
- Close the response body, clear the session ID, and mark the client uninitialized so
isConnected() returns false.
- Surface an ordinary request’s session expiry promptly as a connection failure.
- Preserve the original cancellation/deadline exception when expiry is detected during the cancellation POST.
- Allow a subsequent reconnect to initialize without the expired session ID.
- Keep healthy connections reusable after cancellation or deadline expiry.
The 2025-11-25 specification, Session Management points 3–4, requires fresh initialization after a session-bound 404. It does not require automatically replaying the interrupted tool call.
Additional context
Describe the bug
Stateful Streamable HTTP session expiry is not correctly reflected in the PHP SDK’s connection state.
In the affected
HttpTransport::send()implementation, HTTP status codes are not checked before processing or discarding the response body.Consequently:
Client::isConnected()remainstrue, and subsequent requests continue using the expired session.notifications/cancelledis also discarded without invalidating the connection.This concerns protocol revisions using
Mcp-Session-Id, such as2025-11-25.To Reproduce
Ordinary tool request
S.Son the server.Cancellation or deadline expiry
Sand leave its response pending.notifications/cancelledwithS; the server returns HTTP 404.Simply adding a
ConnectionExceptionfor HTTP 404 is insufficient for the second case:Protocol::notifyCancellation()catches notification failures. Connection invalidation must persist independently of whether that exception reaches the caller.Expected behavior
Mcp-Session-Idas session expiry.isConnected()returnsfalse.The 2025-11-25 specification, Session Management points 3–4, requires fresh initialization after a session-bound 404. It does not require automatically replaying the interrupted tool call.
Additional context