fix(mcp): stop reporting a dropped backend response as success (#3562)

Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com>
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
This commit is contained in:
Henry
2026-09-10 19:09:30 +08:00
committed by GitHub
parent f367c9b125
commit b1b28d72a3
7 changed files with 378 additions and 8 deletions
@@ -56,8 +56,44 @@ client timeout without diagnostic evidence.
action, request, and session for accepted requests and terminal bridge
errors. These logs shall not contain tool arguments.
### REQ-AGENTS-MCP-BRIDGE-RELIABILITY-002: Empty response payload reporting
**Intent:** A backend client must report an empty response payload as a bridge
error when a caller expects a result. It must not turn an empty payload into a
successful result with an unset value.
#### Acceptance criteria
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.1:** When a successful response has
zero payload bytes and the caller supplied a result sink, the backend client
shall return an error.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.2:** The error shall identify the
empty-payload condition and include the action name. It shall not include the
request payload or any tool argument.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.3:** When the caller supplied no
result sink, a zero-byte payload shall keep the existing successful behavior.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.4:** An error response shall keep the
existing backend-error behavior, even when its payload has zero bytes.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.5:** A valid empty object shall keep
decoding successfully. The task-plan tool shall keep its current no-plan
text for that object.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.6:** A valid `null` payload shall keep
its current decoding behavior and shall not become an empty-payload error.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.7:** A non-empty invalid or
unassignable payload shall keep returning its decoding error.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.8:** The channel and dispatcher
backend clients shall use the same response classification rules.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.9:** Each empty-payload error shall
create one warning with the request ID and action. The channel client shall
also include the session ID and duration. Warnings shall not include tool
arguments.
- **AC-AGENTS-MCP-BRIDGE-RELIABILITY-002.10:** The error shall reach the caller
on its first occurrence. This behavior shall add no retry or backoff.
## Out of scope
- Deadlines for task, integration, plugin, or agent-launch business operations.
- A new user-interface alert for a stalled MCP request.
- Traffic between an agent and a third-party MCP server.
- Treating valid `null` payloads as transport errors.
- Reproducing or assigning a root cause to a historical empty-result incident.
@@ -3,6 +3,7 @@ status: current
system: agents
requirements:
- REQ-AGENTS-MCP-BRIDGE-RELIABILITY-001
- REQ-AGENTS-MCP-BRIDGE-RELIABILITY-002
created: 2026-09-04
owners:
- Kandev
@@ -25,6 +26,7 @@ operation keeps its own context and deadline.
| Requirement | Design sections |
| --- | --- |
| `REQ-AGENTS-MCP-BRIDGE-RELIABILITY-001` | [Current dispatcher binding](#current-dispatcher-binding), [Request ownership](#request-ownership), [Failure behavior](#failure-behavior), [Observability](#observability) |
| `REQ-AGENTS-MCP-BRIDGE-RELIABILITY-002` | [Empty response classification](#empty-response-classification), [Failure behavior](#failure-behavior), [Observability](#observability), [Testing strategy](#testing-strategy) |
## Confirmed fault
@@ -127,6 +129,27 @@ This design does not add a common response deadline. Such a deadline can stop
valid clarification waits and long launch operations. Transport ownership and
caller contexts provide the required termination signals.
## Empty response classification
`ChannelBackendClient` and `DispatcherBackendClient` classify a response after
the transport has returned a non-nil message without an error.
Both clients apply this order:
1. An error response returns the existing backend error.
2. A nil result sink returns success without payload decoding.
3. A zero-byte payload returns the shared `ErrEmptyBackendPayload` error.
4. A non-empty payload passes to JSON decoding, which returns any existing error.
The clients use an action-specific wrapped error for the empty-payload case.
The channel client logs the configured session ID and elapsed duration. The
dispatcher client logs only fields that exist at its in-process boundary.
The rule detects an empty response envelope. It does not identify the cause of
the missing payload, validate a response schema, or classify valid `null` data.
Valid `{}`, `[]`, `null`, and other non-empty JSON values keep their current
behavior.
## Observability
The backend records one request-received event at `Info`. The event includes
@@ -153,6 +176,9 @@ Unit tests cover these cases:
- A stream disconnect releases only requests owned by that stream.
- Successful responses, context cancellation, reset, close, and clarification
waits keep their current behavior.
- Both clients return an error for a zero-byte success payload when a result
sink exists, and they preserve the nil-sink, error-response, valid JSON, and
decode-error cases described in [Empty response classification](#empty-response-classification).
Race-enabled tests cover concurrent dispatcher updates, request completion,
stream replacement, reset, and close.