— AI & agents

MCP pagination: why a connected client can miss tools

Trace MCP tools/list across opaque cursors, empty intermediate pages and host caches with an executed seven-tool local collector fixture.

4 min readUpdated 2026-11-05By AptixLabs studio
Page one: a, b, c; Opaque cursor: Forward unchanged; Page two: d, e, f; Final page: g; no continuationAptixLabs · 2026-11-05

Original explanatory figure of a synthetic or proposed design; not a deployed product screenshot. View full-size diagram

An MCP connection can succeed while tool discovery remains incomplete. Collect every tools/list page before comparing the advertised catalogue with the tools shown by the host. Keep cursors opaque, preserve the relevant authorization context and stop only at the documented end condition. A missing tool on page three is a discovery failure even when page one and an unrelated tool call work.

Separate the wire catalogue from the chooser

There are at least three inventories to compare: the server's complete response sequence, the host's cached catalogue and the subset enabled for the current conversation. Count names at each boundary. A permission filter or user setting can legitimately reduce the final list, while a collector that ignores nextCursor silently truncates it. Save a redacted list of tool names and schema hashes rather than assuming the host's visible count represents the server.

The current MCP tools specification defines paginated listing. Read the contract for the revision negotiated or declared by your actual client. The July 2026 revision and older supported revisions differ in other lifecycle details; a successful pagination fixture does not establish compatibility across those eras. Keep the server release, SDK version, host build and supported protocol revision with the inventory.

MCP tools specification: listing, pagination and tool results

A seven-tool catalogue over three pages

Our isolated collector fixture uses fictional tool names a through g. The first two pages contain three names each and return opaque continuation tokens; the third contains g and no nextCursor. The executed result collected all seven names. It also followed an empty intermediate page that still contained a continuation token and rejected a cursor cycle. These are local collector results, not observations of a deployed MCP host.

cursor = None
seen = set()
tools = []
while True:
    if cursor in seen:
        raise ValueError('cursor cycle')
    seen.add(cursor)
    page = fetch_page(cursor)  # authorized, bounded request
    tools.extend(page['tools'])
    if 'nextCursor' not in page:
        break
    cursor = page['nextCursor']  # do not decode or increment

The pseudocode assumes the server uses absence of nextCursor as its final-page contract. Add response-schema validation and a maximum page or elapsed-time budget before using a collector in a product. If your SDK normalizes a terminal cursor, follow its documented representation. An unexpected empty-string cursor is a malformed or provider-defined case to resolve explicitly, not a reason to invent a numeric page offset.

Actual isolated collector observations
FixtureResult
Three normal pagesSeven names collected
Empty intermediate pageContinuation followed
Repeated cursorCycle rejected

Stable ordering needs an owner

A cursor should identify continuation within the server's ordering contract. If tools can change while pages are being fetched, decide whether the server gives a consistent snapshot, a generation token or a documented best-effort view. The collector can detect duplicate names and repeated cursors, but it cannot reconstruct a tool omitted by a changing catalogue. Restarting discovery on a generation change may be appropriate when the provider exposes that signal.

Authorization can also change between pages. Do not fetch page one as one account and the next page under a different token merely because both requests reach the same URL. Store the principal, permitted scope set and endpoint identity with the discovery run. If those inputs change, invalidate the run instead of merging two catalogues whose access rules differ. Tool names in logs can still reveal product capabilities, so retain only what the operational review needs.

Compare once before refreshing caches

After exhausting the cursor sequence, compare names and schemas with the host. If the fresh response contains g but the chooser does not, inspect host caching, enablement and capability support. If g never arrived, investigate the server list or collector first. Refreshing a host cache cannot recover a page the server never sent.

Turn this diagnosis into one small release check: the last expected synthetic tool must appear exactly once, and a permitted read call must work through the supported host. Keep execution separate from discovery: finding a schema does not establish that business authorization permits the call. The saved local fixture and result provide a reproducible starting point; real SDK and host coverage remains a release-specific task.

MCP tool schema versioning

Evaluate the tool action, not just the answer.

Bring one tool workflow, supported hosts and a sanitized failure trace for a scoped AI integration review.

AptixLabs ai agent development

Why can a connected MCP client miss tools?

The client may have collected only the first tools/list page, cached an older catalogue or filtered tools after discovery. Exhaust cursors before comparing host state.

Does an empty page mean listing is finished?

Only if the response meets the documented terminal condition. An empty page can still carry a continuation cursor.

Should a client decode the cursor?

No. Treat it as opaque continuation state and send it back unchanged under the same authorization context.

Need help with ai agent & automation development?

Tell us about your product, existing stack and the problem you need to solve. We can discuss integration scope, implementation tradeoffs and a practical first milestone.

Start a conversation