Repository navigation
Cap agent response size by default to prevent memory DoS #1503
Copy link
Copy link
Open
Description
Current state (main)
- No response-size option exists (
AgentConfiginsrc/adcp/types/core.pyhas onlytimeout). - MCP streamable HTTP and SSE use the httpx client from
_make_hardened_mcp_http_factory(src/adcp/protocols/mcp.py~L112–145): no body limit. - A2A builds a plain
httpx.AsyncClientin_get_httpx_client(src/adcp/protocols/a2a.py~L224–282): no body limit; body reading happens insidea2a-sdk. _MAX_TEXT_SIZE_BYTES = 1_048_576(mcp.pyL92, applied ~L400/L433) runs after the full result is in memory and only skips oversizedcontent[].text;structuredContentis unchecked. It does not prevent OOM.- Existing caps elsewhere (
registry.py,webhook_sender.py, reporting submissions) show the pattern, but not on the agent call path.
Ask
- Add
max_response_bytestoAgentConfigwith a non-zero default (proposed 4 MiB). - Enforce it while streaming, via a size-limiting httpx transport/stream wrapper at the two choke points above (
_make_hardened_mcp_http_factory/_make_custom_mcp_http_factory, and_get_httpx_client), covering SSE per event and per call. Event hooks alone run before the body is read and can't enforce this.
Why
The hosted compliance grader (adcontextprotocol/adcp) was OOM-killed repeatedly: a seller agent answered get_products with a 17.8 MB response (26,163 proposals for 7 products). Process memory went past 10 GB, almost all outside the JS heap, and every agent claimed in the same batch starved. Reproduced locally at 10.5–10.9 GB peak; with a 4 MiB response cap the same run peaks at 1.6 GB and the storyboard grades partial.
Agent responses are untrusted input. A buyer, crawler, or grader using an AdCP SDK should not be able to be memory-DoS'd by one agent, and that protection should not depend on every adopter remembering to opt in.
Proposed default behavior (same across all SDKs)
- On by default for every agent response on the MCP and A2A call paths, including discovery and tool listing. Proposed default: 4 MiB, matching the hosted grader and existing discovery caps. Overridable per agent and per call for agents that legitimately publish large catalogs.
- Fail with a typed error (e.g.
ResponseTooLarge), never silently truncate. - Enforce while reading, counting bytes as they stream in (and pre-reject on
Content-Length). A check after the body is buffered or parsed does not prevent OOM. - Cover SSE / streaming responses: cap each event and the cumulative bytes per call, so a peer can't escape the cap by replying with
Content-Type: text/event-stream. - Count decompressed bytes (or request identity encoding).
Tracking the cross-SDK default and spec wording: see the linked spec issue.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
bokelley commentedon Oct 14, 2026
Spec and cross-SDK tracking: adcontextprotocol/adcp#8190
fix(client): propagate the encoding rejection typed on every path