Skip to content

CLI and mcpdo JSON error envelopes drop the structured Retry-After data from a 429 #2772

Open

Description

@cliffhall

Found during the v2.12.0 release smoke (#2764), while verifying #2442 / PR #2737.

Problem

#2737 annotates a throttled request's error with structured rate-limit data on error.data (AnnotatedRateLimitData in core/mcp/rateLimit.ts: method, retryAfter, retryAfterMs). That is so "any consumer that wants a countdown or JSON output" can act on it. The CLIs' JSON error envelopes drop it, so a script driving them can only scrape the prose message:

# one-shot CLI, tools/list answered with 429 + Retry-After: 30
{"error":{"code":"error","message":"Rate limited by the server (HTTP 429) on tools/list. The server asked to retry after 30s. Error POSTing to endpoint: slow down","cause":"Error POSTing to endpoint: slow down","status":429}}

# mcpdo, same server
{"error":{"code":"error","message":"Rate limited by the server (HTTP 429) on tools/list. The server asked to retry after 30s. Error POSTing to endpoint: slow down"}}

The envelope is built in core/cli/error-handler.ts, which both CLIs share (#2461).

Expected

When the error carries rate-limit data, the envelope adds the structured fields alongside the existing ones, for example:

{"error":{"code":"rate_limited","message":"…","status":429,"method":"tools/list","retryAfter":"30","retryAfterMs":30000}}
  • retryAfter is the header as sent, which AnnotatedRateLimitData already preserves verbatim for a malformed value.
  • retryAfterMs is present only when the header parsed.
  • A missing header omits both fields rather than emitting null, or emits null consistently, whichever the envelope's existing conventions favor.
  • Whether a dedicated code (and exit code) is worth adding for rate limiting is part of this issue to decide. The current "error" / exit 1 is the fallback.

Both CLIs need tests, since the envelope is shared but each client's output path is separate.

Activity

  1. added this to the v2.13.0 milestone on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature requestv2Issues and PRs for v2

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions