fix(mcp): recovered application errors keep the breaker strike
The pick returns the real result after a transport recovery instead of
dropping it, but it also skipped the breaker bookkeeping. Application
errors counting as strikes is the point of the breaker (3ff18ffe14,
#10447: a server answering errors made the model hammer it 8x in 10s).
Route the recovered result through _record_call_outcome so the caller
sees the tool's answer and the counter still moves the right way.
This commit is contained in:
@@ -204,6 +204,9 @@ def test_call_tool_handler_rebuilds_configured_server_transport(
|
||||
assert call_count["n"] == 1
|
||||
else:
|
||||
assert parsed == {"error" if application_error else "result": "reconnected"}
|
||||
# The recovered result is the tool's real answer either way; an application error is
|
||||
# still one breaker strike (#10447), a success resets the counter.
|
||||
assert mcp_tool._server_error_counts.get("resumed", 0) == (1 if application_error else 0)
|
||||
assert call_count["n"] == 2
|
||||
assert routes == [expected_route, expected_route]
|
||||
assert configs == [transport_config, transport_config]
|
||||
|
||||
@@ -132,16 +132,15 @@ def _lookup_reconnectable_server(server_name: str, require_loop: bool = False):
|
||||
|
||||
|
||||
def _retry_once(server_name: str, retry_call, op_description: str, what: str):
|
||||
"""Re-run ``retry_call`` after a recovery step. Returns the result (closing the breaker)
|
||||
when the RPC completed; None when the retry raised (caller falls through)."""
|
||||
"""Re-run ``retry_call`` after a recovery step. Returns the result when the RPC completed
|
||||
(an application error is still the tool's real answer, and still a breaker strike per #10447);
|
||||
None when the retry raised (caller falls through)."""
|
||||
try:
|
||||
result = retry_call()
|
||||
except Exception as retry_exc:
|
||||
logger.warning("MCP %s/%s retry after %s failed: %s", server_name, op_description, what, retry_exc)
|
||||
return None
|
||||
# An application error still proves the recovered transport completed a round-trip.
|
||||
_core._reset_server_error(server_name)
|
||||
return result
|
||||
return _record_call_outcome(server_name, result)
|
||||
|
||||
|
||||
def _handle_auth_error_and_retry(server_name: str, exc: BaseException, retry_call, op_description: str):
|
||||
|
||||
Reference in New Issue
Block a user