Quick answer: When an MCP tool returns a large result set in one response, the practical limit is not abstract; transport frames, model context windows, and client memory all cap what is usable, and an oversized payload either gets truncated silently or pushes the model into a confused state where it acts on an incomplete list. The fix is to design the tool for pagination from the start, return a bounded page with an explicit cursor and a hasMore flag, and let the client or agent fetch additional pages on demand. Truncation should be an explicit, documented signal with metadata, never a silent cut in the middle of a list. If your tool dumps everything and relies on the caller to deal with the size, you are not providing flexibility; you are moving a design problem into the model's context, where it is handled least reliably.

It is tempting to make a tool return the complete set of records so the agent "has everything," but the model is not a database and the MCP transport is not a bulk file channel. A tool result is materialized into the model's working context, where it competes with the rest of the prompt and the other tools, and past a certain size the marginal records actively reduce the quality of reasoning. In real deployments the failure shows up in recognizable ways: the agent summarizes only the first page as if it were the whole result, misses a record that happened to fall past the cut, or hits a transport error and retries the same oversized call. The design goal is therefore to keep each result small and self-describing and to make the full set reachable through a controlled loop.

Where oversized results break down

The limits come from different layers, and treating them as one "too big" problem hides which one you actually hit.

LayerFailure modeDesign response
TransportFrame or message size errorBound every response
Context windowLater records degrade reasoningReturn pages, not dumps
Client memoryLarge payloads stall clientsStream or page on demand
Model behaviorActs on partial listExplicit truncation marker

The model behavior row is the most insidious because no error is raised: the agent quietly treats the visible portion as complete. An explicit marker at least gives the model a fact it can reason about, rather than letting it infer completeness from a payload that was cut.

Design a cursor-based tool contract

Give the tool a page size and a cursor, and return the current page plus the cursor for the next one and a boolean indicating whether more data exists. This keeps every call bounded and makes the agent's loop straightforward.

// request
{ "name": "list_issues", "arguments": { "limit": 25, "cursor": "eyJsYXN0Ijo4NzQyfQ" } }

// response
{
  "items": [ /* at most 25 records */ ],
  "pageInfo": {
    "hasMore": true,
    "nextCursor": "eyJsYXN0Ijo4NzY3fQ"
  }
}

Opaque cursors are preferable to offset numbers for mutable data because the underlying set can change between pages; an offset silently skips or duplicates records when rows are inserted, while a cursor anchored to the last seen record remains stable.

Make truncation explicit when it cannot be avoided

Some tools cannot paginate cleanly because they wrap a source that returns a fixed blob, such as a log file or a search endpoint. In that case, bound the output and attach metadata that says the result was truncated and where the cut happened, so the agent can request the next segment rather than assume the list is complete.

{
  "items": [ /* bounded subset */ ],
  "truncated": true,
  "truncation": { "reason": "max_bytes", "offset": 20000, "total": 84000 },
  "nextCursor": "b2Zmc2V0OjIwMDAw"
}

The schema for the tool should describe these fields so the model knows the shape it will receive and the meaning of the cursor. A vague tool description with a loose result schema is precisely what leads agents to invent pagination behavior or assume a partial list is exhaustive.

Test the paging loop as a behavior

Beyond unit tests on individual pages, test that a caller following cursors walks the full set and stops exactly when hasMore is false, and that a page boundary does not duplicate or drop records. The contract for the tool should declare the page size limit and the meaning of the cursor so generated clients and agent prompts can rely on a stable interpretation rather than discovering it empirically.

The practical rule is to treat result size as part of the tool's interface, not an afterthought: bounded pages, opaque cursors, an explicit hasMore flag, and documented truncation with a continuation path. A tool that returns the entire dataset pushes the problem into the model's context and gets the least reliable handling available. Keep each response small and self-describing, and let the agent fetch more deliberately, and the agent's conclusions reflect the full set rather than whichever slice happened to fit.

Frequently asked questions

Why not just return all records so the agent has everything?

The tool result enters the model's working context, where an oversized payload degrades reasoning and may be truncated. Pagination keeps the relevant slice small while making the full set reachable through a controlled loop.

What is the difference between truncation and pagination?

Pagination is a designed, cursor-based walk through the data. Truncation is a cut, which should be explicit and include a continuation path rather than silently dropping the tail of the list.

Why use a cursor instead of an offset?

Offsets skip or duplicate records when the underlying set changes between pages. An opaque cursor anchored to the last seen record stays stable as rows are inserted or removed.

How does the model know a partial list is not complete?

Return a hasMore flag and, when a cut is unavoidable, a truncated marker with metadata and a next cursor. Without that signal, the agent tends to treat the visible portion as the whole result.

What should the tool schema include?

A limit and cursor on the input, and on the output the items, a nextCursor, and a hasMore boolean, plus any truncation metadata, so the response shape is explicit and stable.

This is part of the The MCP Server Guide — start there for the full picture of building reliable MCP servers.