Releases: develeap/hyperping-python
v1.7.0 — MCP rate-limit handling, init cool-off, ensure_initialized()
Highlights
This release hardens how the MCP client deals with the Hyperping server's initialize rate limit. Until now, a rate-limit on the MCP handshake came back as a generic HyperpingAPIError that callers couldn't dispatch on, and a tight retry loop on a fresh HyperpingMcpClient could burn through the bucket in seconds. After 1.7.0 the SDK detects, types, and cools-off these signals correctly.
Why upgrade
-
Typed rate-limit errors on MCP
initialize. The Hyperping MCP server signals its undocumentedinitializecap by returning HTTP 200 with JSON-RPCerror.code = -32000(not 429). The SDK now classifies that asHyperpingRateLimitErrorwithretry_afterparsed from the message, soexcept HyperpingRateLimitErrorworks just like it does for the REST 429 path.from hyperping import HyperpingMcpClient, HyperpingRateLimitError try: mcp.get_status_summary() except HyperpingRateLimitError as e: time.sleep(e.retry_after or 30)
-
Initialize cool-off latch. After a rate-limited handshake, subsequent calls on the same client short-circuit with
HyperpingRateLimitErrorand issue zero further HTTP requests until the advertisedretry_afterelapses. Stops accidentally burning more slots from the bucket. -
ensure_initialized()on both clients. Lets you perform the MCP handshake explicitly at service boot so you fail fast on cold-start rate-limits instead of failing on the first business call.mcp = HyperpingMcpClient(api_key="sk_...") try: mcp.ensure_initialized() # raises HyperpingRateLimitError if capped except HyperpingRateLimitError as e: print(f"cold-start rate-limited; retry in {e.retry_after}s") raise
-
Concurrency fix. Closed a TOCTOU race in lazy
initializewhere two concurrent first calls on oneHyperpingMcpClientcould each POSTinitialize. Now exactly one handshake fires, guarded by a dedicated lock with a lockless fast path so post-handshakecall_tooldoesn't contend. -
status_codepreservation through cool-off. When the latch short-circuits, the raised exception carries the originating status (200for JSON-RPC-32000,429for HTTP 429) so callers can distinguish the two buckets. -
Notification-leg classification. A rate-limit signal returned on the
notifications/initializedleg of the handshake is now classified too, instead of being silently swallowed. -
New README section: "MCP rate limits and connection lifecycle" with operational guidance: one long-lived
HyperpingMcpClientper process, avoid instantiating in a loop, and how multiple workloads on one API key collide on theinitializecap.
Server-side caveats (not fixed here, by design)
The Hyperping MCP server still:
- Returns HTTP 200 + JSON-RPC
-32000for rate-limit (instead of HTTP 429). - Enforces an undocumented per-key cap on
initialize(observed ~5/minute) on top of the documented 300/min shared with REST.
These are tracked for upstream feedback; this release is pure client-side mitigation so existing users get value today.
Full changelog
Added
ensure_initialized()onHyperpingMcpClientandAsyncHyperpingMcpClientfor startup health checks. Performs the MCP handshake now if it hasn't happened yet and raisesHyperpingRateLimitErrorif the server'sinitializecap is hit.- New "MCP rate limits and connection lifecycle" section in README documenting Hyperping's stateless MCP server, the undocumented
initializecap, and the recommended client lifetime per process.
Fixed
- MCP rate-limit errors that the server returns as HTTP 200 with JSON-RPC
error.code = -32000(notably theinitializeper-minute cap) are now classified asHyperpingRateLimitErrorwithretry_afterparsed from the message, instead of a genericHyperpingAPIError. Existing HTTP 429 handling is unchanged. - After a rate-limit on
initialize, the MCP transport latches a cool-off so subsequentcall_toolinvocations short-circuit withHyperpingRateLimitErroruntil the advertisedretry_afterelapses, instead of issuing further HTTP requests that would burn more slots from the bucket. - TOCTOU race in lazy
initializewhere two concurrent first calls on the sameHyperpingMcpClientcould each POSTinitialize. The handshake is now performed under a dedicated lock with a double-checked flag, including a lockless fast path so post-handshakecall_tooldoes not contend on it. - Cool-off short-circuit now preserves the originating status code (200 for JSON-RPC
-32000, 429 for HTTP 429) so callers can distinguish buckets, andretry_afterusesmath.ceilto avoid over-reporting by one second. - JSON-RPC rate-limit signals returned on the
notifications/initializedleg are now classified asHyperpingRateLimitError(previously they were silently treated as a successful notification). - Rate-limit detection requires the message to contain
"rate limit exceeded"(the observed phrasing) to avoid false positives on unrelated server messages that happen to mention"rate limit". TheRetry-Afterparser now also acceptsRetry-After:andretry after N secondsvariants.
Compatibility
- Public API additive only (
ensure_initialized()on both MCP clients). - Existing
HyperpingRateLimitErrorconsumers continue to work; the same exception type is now raised on a wider set of server responses, withstatus_codeset to whichever signal was used (200or429). - Python 3.11 / 3.12 / 3.13. No new dependencies.
Install
pip install hyperping==1.7.0
# or
uv add hyperping==1.7.0Links
- PyPI: https://pypi.org/project/hyperping/1.7.0/
- Full diff: v1.6.0...v1.7.0