Skip to content

fix: MCP safe reads do not retry transport failures or preserve diagnostics #4

Description

@viowai

What Happened

@everme/agent-sdk@0.3.4 documents one retry for safe transport failures, but the retry branch is unreachable for native fetch failures:

  1. execOnce() catches the native failure and wraps it in EvermeError.
  2. execWithRetry() immediately rethrows every EvermeError.
  3. A synthetic local socket-reset probe therefore observes one request, not two.

There is a second semantic gap: /mem/search and /mem/context are safe reads implemented as POST, so method-only retry gating cannot cover them. When the error reaches @everme/memory-mcp, the adapter returns only error: <message> and drops correlation/classification data that an MCP host could use for health decisions.

This is separate from EverMind-AI/EverOS#338, which tracks ingestion task metadata and exact visibility verification.

Steps To Reproduce

  1. Use @everme/agent-sdk@0.3.4 or current main at 0e6fd0ec90a2cf8a90946f434504b6813826eb1a.
  2. Point createClient() at a local HTTP server that resets the first GET connection and succeeds on the second.
  3. Call client.request("GET", "/healthz").
  4. Observe that the server receives one request and the caller receives EvermeError immediately.

The same dead path applies to DNS/connection errors. POST /mem/search and POST /mem/context are never eligible under the current method-only check.

Expected Behavior

  • Explicit semantic reads (GET/HEAD, plus POST /mem/search and /mem/context) should make at most one bounded retry for transient transport failures, HTTP 429, and HTTP 5xx.
  • All attempts and Retry-After delay should share the original timeout budget.
  • Non-idempotent memory writes must remain single-attempt, even if a caller mislabels one as a read.
  • Exhausted errors should expose redacted fields such as classification, cause code, HTTP status, request id, attempts, retryability, and elapsed time.
  • MCP tool failures should preserve those fields in structuredContent.error while retaining the existing isError text response.
  • Diagnostics must not include queries, request bodies, URLs, tokens, or native fetch cause text.

Environment

  • Package: @everme/agent-sdk@0.3.4, @everme/memory-mcp@0.3.4
  • Runtime: Node.js 18+
  • Reproduction: synthetic localhost transport failure; no production data involved

Proposed Fix

  • Add explicit safe_read / non_idempotent_write request semantics.
  • Allow POST safe-read opt-in only for a hard allowlist of /mem/search and /mem/context.
  • Retry safe reads once within a shared deadline; do not start a second full timeout.
  • Preserve structured, sanitized EvermeError metadata through the MCP adapter.
  • Add regression coverage for socket reset, DNS failure, timeout budget, 429, 5xx, 401/auth classification, and write single-attempt behavior.

Logs

No production logs are attached. The reproduction uses placeholder credentials and a local synthetic endpoint only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions