The way an agent dies is rarely the way you expect. You budget for the context window, you count your system prompt and your history, you leave headroom — and then a routine search_logs call matches fifty thousand rows, the whole payload lands in one turn, and the session aborts before the model reads a word of it. The window wasn't too small. One tool returned too much.
This is a different problem from the one everyone worries about. It isn't context rot, where a full-but-legal window quietly degrades recall, and it isn't a truncated model response, where the model's own output gets cut. It's a single inbound payload that busts the budget on arrival. And it lives at a boundary the protocol quietly declined to defend. The Model Context Protocol paginates the things you'd expect an agent to enumerate — tools/list, resources/list, prompts/list all take cursors. But there is no pagination, and no size limit, for a tool result. A GitHub discussion that has been open long enough to become a landmark puts it plainly: MCP servers "can sometimes return very large responses," clients forward them straight into the model, and the spec offers no mechanism to stop it. Overflow strikes exactly where the standard is silent.
Truncation is the fix that makes it worse#
The first instinct is almost universal: cap the response string at some length and cut. It is the one option that is strictly worse than doing nothing, and it's worth being precise about why.
Cutting a byte stream at a fixed length has no respect for structure. A JSON array sliced at character 40,000 is no longer JSON; it's a prefix that a parser rejects, and a model that then has to guess where the data ended. But the deeper damage is semantic. Truncation destroys the model's ability to distinguish two states that demand opposite responses: the query returned nothing and the query returned so much it was clipped. Those look identical after the cut. The agent that should have narrowed its filter instead concludes the table is empty and reasons forward from a false premise — confidently, because nothing in the context signals that anything was lost.
A truncated tool result isn't a smaller answer. It's a wrong answer wearing the costume of a complete one.
If you must truncate, truncate only flat prose whose tail you can afford to throw away, and say so in-band: append a marker the model can see — [... 48,213 more rows omitted; call again with a filter] — so the clip becomes a signal instead of a silent lie.
The three fixes that actually hold#
Paginate and filter at the tool boundary. The cleanest move is to never generate the oversized result in the first place. Anthropic's guidance in Writing effective tools for agents is to give tools a page cursor and a response-format switch — a concise mode that returns IDs and a one-line summary per item, a detailed mode the agent opts into only for the handful of rows it cares about. The design cost is real: the tool author has to decide what a "page" and a "summary" mean for their data. That decision is the work.
Hand back a handle, not the payload. For results the agent will sample or grep rather than read whole, return a reference: a file path, a resource ID, a stored-query token — plus a small preview and the metadata that lets the model decide what to do next (a row count, a schema, the first three matches). Anthropic calls the runtime move just-in-time retrieval: keep lightweight identifiers in context and pull the underlying content in only when needed, with the exact tokens needed. It is precisely how Claude Code reads a 2 MB log — it doesn't ingest the file, it greps it, then heads the matches. The bulk stays on disk; the context holds a pointer and a plan. Hosts already do a crude version of this on their own when a tool overruns: they write the output to a temp file and hand the model the path, because the protocol left them no better option.
Offload the bulk to code, or out of band entirely. When the answer requires chewing through data that's too big to read at all — a join, an aggregation, a transform — the data shouldn't touch the model's context at any point. Route it into a code-execution sandbox and let the agent write the query that filters and summarizes, returning only the small result. Anthropic's code execution with MCP work reports large drops in tokens consumed by keeping raw data in the execution environment instead of the conversation. It's the same instinct behind every serious effort to reduce an agent's token bill: the cheapest token is the one that never enters the window. For results bound for a human rather than the model, structured content and a download URL can render an interactive table that never enters the context window at all.
The inversion underneath#
All three share one shape, and it's the actual lesson. The default assumption — that a tool's job is to return everything it found and let the model sort it out — puts the burden of relevance on the tool author, at design time, blind to the specific question. Every durable fix moves that burden to the agent, at run time, when it actually knows what it's looking for. The tool's job is not to answer the query; it's to return the smallest high-signal thing plus a way to get more.
Which means the real fix for a tool result too large for the context window is to stop thinking of the tool as a thing that returns data, and start thinking of it as a thing that returns the shortest path to the data. The rows can wait on disk. What the model needs first is the count, the preview, and the handle — and the good sense to know it hasn't seen the rest yet.



