You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Client] Parse JSON-RPC error bodies before falling back to status codes
HttpTransport::send() now checks the HTTP status of every response. On a
non-2xx status it parses the body first: when the body is a JSON-RPC error
answering an outstanding request (404 + -32601 method-not-found, 400 +
-32020 HeaderMismatch, 400 + UnsupportedProtocolVersionError listing the
supported versions), it is dispatched through the normal message path so
the request resolves with the server's error (surfacing as RequestException)
instead of a transport exception.
Bodies that cannot be parsed fall back to two new exception classes, both in
the Mcp\Exception namespace and extending ConnectionException so a failing
handshake keeps being retried by Client::connect():
- SessionExpiredException: HTTP 404 on a request carrying a session id whose
body is not a JSON-RPC error means the server dropped the session. The
transport clears the local session id, marks the client un-initialized so
isConnected() reports false, and throws so the application re-initializes.
- HttpTransportException: any other non-success status, carrying the status
code and a snippet of the response body.
The transport also sends the negotiated Mcp-Protocol-Version header on every
request, including the DELETE that closes a session (shared buildHeaders()),
and runRequest() releases its active fiber, progress callback, and stream in
a finally block even when a request throws.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,7 @@ All notable changes to `mcp/sdk` will be documented in this file.
5
5
0.8.0
6
6
-----
7
7
8
+
* `HttpTransport` now inspects the HTTP status of every response. A non-2xx status whose body is a JSON-RPC error response (404 + `-32601` method-not-found, 400 + `-32020` HeaderMismatch, 400 + UnsupportedProtocolVersionError) is dispatched through the normal message path and surfaces as a `RequestException`, while bodies that cannot be parsed fall back to two new exception classes. `Mcp\Exception\SessionExpiredException` covers a 404 on a request carrying a session id whose body is not a JSON-RPC error (the server has dropped the session; the client clears its session id and marks itself un-initialized), and `Mcp\Exception\HttpTransportException` covers any other non-success status, carrying the status code and a snippet of the body. Both extend `ConnectionException`, so a failing handshake keeps being retried by `Client::connect()`. The transport also sends the negotiated protocol version header on every request, including the `DELETE` that closes the session, and `runRequest()` no longer leaks the active fiber and progress state when a request throws.
8
9
* Always emit `{}` for empty tool schemas: `Tool` recursively normalizes every empty sub-schema — `properties`, `items`, `additionalProperties`, `$defs`, combinators and the other draft-07 to 2020-12 schema keywords — in the constructor, for both `inputSchema` and `outputSchema`, so an object position is never serialized as `[]`.
9
10
* Prompt generators returning content as typed arrays (`['type' => 'text', ...]` etc.) no longer lose the optional fields: `annotations` on every content type, and `_meta` and an explicit `mimeType` on embedded resource contents, now carry through to the resulting `PromptMessage` instead of being silently dropped. A missing resource `mimeType` still defaults to `text/plain`/`application/octet-stream` as before.
10
11
* Add `annotations` support to `ImageContent` (constructor, `fromArray()`, `fromFile()`, `fromString()`, `jsonSerialize()`), matching `TextContent` and `AudioContent`.
$this->logger->warning('Server no longer knows the current session (HTTP 404); clearing the session id so the client re-initializes.', ['session_id' => $this->sessionId]);
242
+
$this->sessionId = null;
243
+
$this->state?->setInitialized(false);
244
+
245
+
thrownewSessionExpiredException('The MCP session no longer exists (HTTP 404); re-initialize the client to start a new session.');
246
+
}
247
+
248
+
// Any other non-success status is a transport-level failure. Reading
249
+
// the body here also surfaces plain-text error pages instead of
250
+
// silently dropping them and leaving the caller waiting on a timeout.
0 commit comments