Blog · · 10 min read
AI agent tool pagination without silent data loss

AI agent tool pagination fails when the model cannot tell whether it saw every matching record. A tool may return the first page, trim a valid response at a byte limit, or keep loading until the context overflows. The agent can then claim that no matching order, incident, or account exists even though it never inspected the full set. This guide defines a result contract with source-side filters, stable snapshots, scoped handles, explicit completeness, bounded page reads, and full-record lookup. It also gives you failure rules and tests that catch skipped, duplicated, hidden, or unauthorized data before the tool reaches production.
The result must say what was not returned
A large result creates context pressure and weakens the evidence behind the agent's answer.
Suppose an agent asks a product tool for failed payouts from the last 30 days. The database has 2,400 matches. The tool returns 100 rows and omits pagination metadata. The model finds no payout for one customer and reports that none failed. Its conclusion sounds precise, but the tool never gave it enough evidence.
A complete result contract must answer five questions:
- What query and authorization produced this result?
- Which stable dataset or snapshot is being traversed?
- How many items were returned, and may more exist?
- How can the caller retrieve another page or one full record?
- Which budget or policy stopped further inspection?
The current Model Context Protocol pagination specification defines opaque cursors, a nextCursor, server-selected page sizes, and invalid-cursor errors. It applies that mechanism to list operations such as tools/list and resources/list. It does not define pagination for arbitrary business records inside a tools/call result. Your tool therefore needs an application-level contract for those records, even when its transport is MCP.
Why large tool results break in different ways
Returning everything moves the failure downstream
Fetching every upstream page can move the limit from the product API to the tool server, network envelope, conversation store, or model context.
An Oracle workflow example follows all API pages and flattens the collected data into one final model input. That can work for a bounded dataset. It does not establish a safe upper bound or stable snapshot when the result grows or changes during traversal.
OpenAI's context-engineering guidance warns that redundant tool results can overwhelm even a large context. It also notes that trimming by recent items can fail when one recent tool result is itself huge, while summarization can lose or distort details. Context management is necessary, but it cannot replace a truthful data contract.
Silent truncation corrupts meaning and shape
Truncation may remove the row that answers the user's question. It may also turn a schema-valid result into an invalid one.
A Cloudflare Agents issue report describes structured output values being replaced by truncation markers. In the reported path, that broke the declared schema and later replay. Treat this as a report about a specific implementation, not a guarantee about every runtime. Put truncation state beside the typed payload. Do not insert an arbitrary marker where a number, object, or required string should be.
A Kubernaut issue report records an agent repeating a list call seven times after result truncation. The repair removed duplicate payload data, preserved a total count, and bounded the full response envelope by bytes. Retries need a new cursor or narrower query. Repeating the same oversized call is not recovery.
Medium results can overflow in aggregate
A per-tool limit is not enough. Five responses that each fit can still exceed the remaining run budget. An OpenClaw issue report describes this aggregate case and a session that could no longer recover.
Enforce two limits:
- a per-result byte and token budget before a tool result enters the conversation;
- an aggregate budget for unresolved tool data in the current run.
The aggregate check must happen before dispatching another broad query, not after the model context is already full.
Build an AI agent tool pagination contract
Use separate operations for query creation, page retrieval, and full-record retrieval. Do not overload one tool with several incompatible response shapes.
Start with a filtered query, not a bulk dump
The first operation should accept the narrowest fields your product can enforce at the data source:
- tenant and user context from trusted runtime state, never model arguments;
- time range and status filters;
- stable sort keys;
- a requested projection or named view;
- an upper page-size hint that the server may reduce.
Reject an unbounded scan when the product can ask for a time range, owner, state, or identifier. Source-side filtering prevents irrelevant records from entering the tool envelope and reduces the chance that sensitive columns reach the model.
Return a summary and handle before returning many rows. The handle should be opaque to the model and scoped to the authenticated principal, tenant, normalized query, sort order, and snapshot.
{
"query": {
"status": "failed",
"created_from": "2026-08-01T00:00:00Z",
"created_to": "2026-08-27T00:00:00Z"
},
"result_handle": "rh_7Qf9...",
"snapshot_at": "2026-08-27T10:42:31Z",
"summary": {
"matching_count": 2400,
"returned_count": 50,
"has_more": true
},
"page": {
"items": [],
"next_cursor": "c_4mx..."
},
"limits": {
"page_size_applied": 50,
"max_pages_per_run": 8,
"response_bytes": 9120,
"truncated": false
}
}
Keep truncated false when the returned items array is complete for that page. Use has_more to say that the result set has additional pages. These fields describe different facts.
Bind cursors to a stable view
Offset pagination is easy to implement, but inserts and deletes can shift records between requests. The Microsoft MCP pagination guide recommends cursor-based approaches for large or changing datasets and lists consistency, invalid cursors, empty pages, and resource limits among the cases to test.
Choose one consistency rule and expose it:
- Snapshot: freeze the eligible record versions for the handle's lifetime.
- High-water mark: bind the query to an upper timestamp or monotonically increasing key.
- Declared live traversal: permit changes, but state that counts and membership may move.
Snapshot or high-water-mark traversal is usually safer when the agent must prove it checked every record. A cursor should encode or reference the result handle, last stable sort key, direction, and integrity protection. It should not expose raw SQL offsets, tenant identifiers, or authorization claims.
Every page request must reauthenticate the caller and reauthorize the handle. Revoking access after page one must block page two. Do not trust authorization copied into a long-lived cursor.
Separate page summaries from full records
Most tasks do not need every field for every row. Return a compact page with stable identifiers and fields needed for screening. Add a separate get_result_record operation for the selected item.
This pattern keeps the page schema predictable and limits sensitive data exposure. It also makes citations and action receipts clearer because a later decision can refer to the exact record version retrieved in full.
A Temporal Deputy proposal uses the same basic split for large scan results: create a scan handle, return summary counts, page through filtered findings, and fetch one full finding. The issue explicitly notes that protocol-level MCP pagination does not cover arbitrary tool results. Treat the proposal as implementation evidence, not an adopted cross-platform standard.
Make early stopping explicit
An agent should not read 200 pages by default. It should also not imply exhaustive inspection after reading two.
Give each task a stopping policy:
- Find one: stop after a matching record is verified.
- Find up to N: stop after N verified records and report that more may exist.
- Aggregate: use a server-side aggregate instead of loading raw rows.
- Exhaustive check: traverse every page within a declared snapshot and fail incomplete if the budget expires.
The final answer should carry a completeness statement such as verified_match, complete_no_match, partial_no_match, or incomplete_budget_exhausted. Only complete_no_match supports the claim that no matching record exists.
Research on context-window overflow in AI agents proposes storing arbitrary-length tool output outside the active context and giving the agent pointers to it. That is useful for retention and later retrieval. A pointer still needs scope, expiry, authorization, version, and completeness metadata before it becomes a safe product result handle.
Handle failures without restarting the scan
Return typed failures that tell the orchestrator whether to retry, narrow the query, restart from a new snapshot, or stop.
| Failure | Required behavior | Unsafe behavior |
|---|---|---|
| Cursor invalid or tampered | Reject and log the handle and principal | Guess or restart silently |
| Handle expired | Return expiry and a restart option | Reuse stale authorization |
| Permission revoked | Deny the next page and terminate traversal | Finish from cached grants |
| Snapshot unavailable | Mark the result incomplete | Switch to live data silently |
| Page exceeds byte budget | Reduce page size before serialization | Cut bytes inside typed values |
| Aggregate context budget reached | Stop with an incomplete receipt | Dispatch the same broad tool again |
| Duplicate or skipped sort key | Fail the completeness check | Hide the anomaly from the model |
| Full record changed after summary | Return the new version and require re-evaluation | Act on the stale summary |
The agent runtime should own retry budgets. A tool can return a retryable transport error, but it should not launch an unbounded internal loop that hides elapsed time and page progress. An Azure MCP author report describes a large result causing repeated tool calls until the issue was acknowledged and migrated. Persist page progress outside the model's prose so a retry resumes from a known cursor instead of asking the model to reconstruct state.
Verify completeness, security, and context use
Test the contract at three layers.
Data-layer tests
Create fixtures with stable identifiers and deterministic sort keys. Cover:
- zero rows, one row, exactly one page, and one item over a page boundary;
- insertions and deletions between requests;
- equal primary sort values that require a unique tie-breaker;
- duplicate delivery and skipped-record detection;
- snapshot expiry and restart behavior;
- a total count that differs from traversed count.
For an exhaustive traversal, assert that every fixture identifier appears exactly once and that the final receipt says complete.
Authorization tests
Use two tenants and two users with different row access. Try to replay a handle and cursor across each boundary. Revoke access between page requests. Change a row's classification before full-record retrieval. The expected result is denial or explicit re-evaluation, never stale success.
Log the authenticated principal, tenant, normalized query hash, handle ID, snapshot, page sequence, returned counts, byte counts, and terminal completeness state. Do not log raw sensitive rows merely to prove pagination worked.
Agent-behavior tests
Give the model tasks with different stopping rules. Verify that it:
- narrows an unbounded query before scanning;
- uses
has_morerather than guessing from page length; - does not repeat an identical oversized request;
- fetches a full record before making a high-impact decision;
- reports partial inspection when its budget ends;
- claims no match only after a complete traversal or authoritative server-side aggregate.
Run one adversarial case where page one contains a plausible answer but a later page contains the decisive contradiction. This catches agents that stop because the first answer looks convenient.
Common implementation mistakes
MCP list pagination covers tools, prompts, and resources. It does not automatically paginate records returned by a business tool.
A trimmed response is not a page. It has no guaranteed continuation point or complete page boundary.
Keep the total count, has_more, truncation state, and cursors in typed fields so validators can inspect them. Do not hide these values in prose.
Tenant and user scope come from authenticated runtime state. The model may request narrower filters, never broader authority.
Records can move between offsets in a mutable dataset. Bind traversal to a snapshot or high-water mark and a unique stable sort key.
Track the aggregate context budget before the next tool call. A valid individual result can still push the full run past its limit.
Implement one safe path first
Pick the highest-volume list or search tool in your product. Add a scoped result handle, stable snapshot rule, opaque cursor, compact page schema, full-record lookup, and terminal completeness receipt. Then run the page-boundary, mutation, cross-tenant replay, revocation, byte-limit, and aggregate-context tests above.
Do not ship until a test proves the negative case: the agent cannot say "no matching record exists" after inspecting only part of the result set. A partial traversal must end with a partial or incomplete status, never complete_no_match.
References
- Model Context Protocol pagination specification supports the protocol cursor semantics and the boundary between list operations and arbitrary tool results.
- Microsoft MCP pagination and large-result guidance supports cursor choices, source-side paging, consistency concerns, limits, and edge testing.
- OpenAI context engineering with session memory supports the context-pressure, trimming, and summarization risks around large tool outputs.
- Oracle's paginated workflow example provides directly competing implementation coverage and the all-pages aggregation pattern discussed above.
- Solving Context Window Overflow in AI Agents supports externalizing arbitrary-length tool output behind retrievable pointers.
- Cloudflare Agents issue 2014 is the cited author report about schema-breaking silent truncation.
- Temporal Deputy issue 117 is the cited implementation proposal for scan handles, paged summaries, and full-record lookup.
- Kubernaut issue 1434 is the cited author report about truncation-driven retries and the envelope-size repair.
- OpenClaw issue 113701 is the cited author report about aggregate tool-output overflow.
- Azure MCP issue 470 is the cited author report about repeated tool calls after a large result.