feat(mcp): always-explicit retryable on structured errors + transient inference (P1-1) (#875)
Agents could not distinguish 'keep retrying' from 'stop, this is
broken' (agent.feedback): retryable was emitted only when a registry
entry declared true — absent otherwise, including for genuinely
transient DB/network failures whose SQLSTATE is lost when tools wrap
them as Error('Database error: ...').
- StructuredError.retryable is now a required boolean. Registry
declaration wins; otherwise isTransientFailure() infers from Postgres
SQLSTATEs (40001/40P01/57014/08xxx/53xxx/55P03), upstream HTTP
statuses (408/429/5xx), and message signatures that survive wrapping
(deadlock, serialization, statement timeout, fetch/socket failures).
- Unclassified transient failures surface as stable code
TRANSIENT_ERROR (new registry entry, retryable: true).
- categorize_transaction accepts idempotency_key — the tool agents
blind-retry after client-side approval-elicitation drops; the key
makes that retry replay-safe instead of double-staging.
- Contract documented in .claude/rules/mcp-server.md. The planned
'kind' field was dropped: the code registry already encodes it;
retryable is the agent-actionable bit.
Every tool error already flows through the single dispatch point
(toToolError -> getStructuredError), so coverage is universal without
per-tool migration. Full unit suite: 6575 tests green.
Part of dev_docs/mcp_optimization_plan.md (P1-1).
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
001de3c844
commit
250cc7c450
@@ -47,6 +47,15 @@ const GENERIC: Record<string, StructuredErrorEntry> = {
|
||||
message_sv: 'Något gick fel. Försök igen.',
|
||||
message_en: 'An unexpected error occurred.',
|
||||
},
|
||||
// Unclassified-but-transient failures (DB deadlock/timeout, connection
|
||||
// drop, upstream 5xx/429) inferred by isTransientFailure() when no
|
||||
// specific code applies. Stable code so agents can dispatch on it.
|
||||
TRANSIENT_ERROR: {
|
||||
httpStatus: 503,
|
||||
message_sv: 'Tillfälligt fel — försök igen om en stund.',
|
||||
message_en: 'Transient failure — retry the same request after a short backoff.',
|
||||
retryable: true,
|
||||
},
|
||||
INTERNAL_ERROR: {
|
||||
httpStatus: 500,
|
||||
message_sv: 'Ett oväntat serverfel uppstod. Försök igen senare.',
|
||||
|
||||
Reference in New Issue
Block a user