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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user