Skip to content

Cap agent response size by default to prevent memory DoS #1503

Open

Description

@bokelley

Current state (main)

  • No response-size option exists (AgentConfig in src/adcp/types/core.py has only timeout).
  • 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.AsyncClient in _get_httpx_client (src/adcp/protocols/a2a.py ~L224–282): no body limit; body reading happens inside a2a-sdk.
  • _MAX_TEXT_SIZE_BYTES = 1_048_576 (mcp.py L92, applied ~L400/L433) runs after the full result is in memory and only skips oversized content[].text; structuredContent is 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_bytes to AgentConfig with 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)

  1. 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.
  2. Fail with a typed error (e.g. ResponseTooLarge), never silently truncate.
  3. 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.
  4. 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.
  5. Count decompressed bytes (or request identity encoding).

Tracking the cross-SDK default and spec wording: see the linked spec issue.

Activity

  1. bokelley commented on Oct 14, 2026

    @bokelley
    ContributorAuthor

    Spec and cross-SDK tracking: adcontextprotocol/adcp#8190

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions