Build1 distinct publisher3 min readUpdated
In the final 2026-07-28 MCP spec, an empty nextCursor means keep going. A falsy check ends the loop instead, and the call still returns success.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Follow any of these and your For You feed starts watching them — no settings page required.
Two responses reach a C# client looking almost the same. One has no `nextCursor` field at all, which a nullable DTO renders as `null` and which means traversal is finished. The other carries `nextCursor` as the empty string, which means issue another request with the cursor set to `""` [5][7]. `string.IsNullOrEmpty` gives the same answer to both [7]. Nothing downstream looks wrong: the call succeeded, page one deserialized, the list is not empty, and no error mentions the pages that were never asked for [8].
The fixture in the writeup is three deterministic pages of one tool each. The first request omits the cursor, page one hands back `""`, page two hands back `opaque:/+==`, and page three omits the field entirely [9]. The falsy loop collects `catalog.search` and returns [10], which is one tool out of three, so two thirds of that catalog is invisible while the transport reports success [1].
The second cursor's punctuation is there on purpose: a client that decodes, trims, normalizes or reconstructs the token is breaking the same contract in a different place [16], and an unrecognized cursor is supposed to come back as `-32602`, the specification's recommended Invalid params response [17]. That is the loud variant. Someone gets paged for it. The empty-string variant produces no signal at all.
A nullable string models the field correctly, since absence maps to `null` while `""` stays non-null [13]. The distinction usually dies above the loop rather than inside it. A custom converter, a DTO mapper, or a convenience helper such as `NullIfEmpty` can flatten `""` to `null` before any pagination code runs [14]. So a reviewer can read a correct loop and still ship the truncation. The related trap is explicit JSON `null`, which sits outside the 2026-07-28 cursor schema, and which the author argues a strict mapper should reject rather than quietly treat as a terminator [15].
The corrected loop returns only when `NextCursor` is `null`, forwards both `""` and `opaque:/+==` without altering the string, and adds cancellation plus a page cap described as an application guard rather than anything inferred from the cursor [19]. The test discipline matters more than the loop shape: the author prefers assertions on the sequence of cursor values a client requested, because a final item count proves nothing when the server owns page size [11]. The sample runs eight checks with no MCP server, no credential, no model call, no clock and no random input [18], which is the property that makes it a regression test instead of an occasional red build. As a footnote on reading the excerpt literally, the JSON in the post is a pagination fragment; a conforming message still needs its JSON-RPC envelope and the `_meta` that every 2026-07-28 request requires, none of which changes the termination rule [20].
Ranked by verification strength, evidence, and original report placement.
In the final 2026-07-28 MCP specification, cursors are opaque strings; an empty string is a valid cursor and only a missing nextCursor ends traversal.
MCP uses cursor pagination for tools/list, prompts/list, resources/list and resources/templates/list.
The server chooses each page size, so a client cannot infer completion from the number of returned items.
The official pagination specification's rule is to continue whenever the response supplies a non-null nextCursor, and the client must pass that token back without parsing or changing it.
In a nullable C# response model, an absent nextCursor field is represented by null.
A common C# do/while loop terminates on !string.IsNullOrEmpty(cursor), which treats a response whose nextCursor is the empty string as the last page.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Reproducible code, single-source spec reading
The mechanism is demonstrated rather than asserted: broken and corrected C# loops, a deterministic three-page fixture, and a runnable sample with eight checks plus a merged PR recording validation commands. What is not independently evidenced is the protocol reading itself - the empty-cursor rule, the -32602 recommendation and the cursor schema all come from one author's paraphrase, and the normative 'reject explicit null' point stays at argument level.
Spec and SDK release signal only
Adoption evidence is limited to two artefacts: the author's reference to the 2026-07-28 release announcement stating the spec is released with Tier 1 SDKs updated, and the author's own verifier sample and merged PR. Nothing in the cluster shows how many real clients paginate incorrectly, whether any shipped SDK or wrapper exhibits the truncation, or that any downstream project adopted the empty-cursor regression fixture.
Slightly overstated framing, well-scoped body
The headline framing that the bug 'hides most of a tool catalog' rests on a synthetic fixture where each page holds exactly one tool, so the two-thirds figure is a property of the fixture rather than a measured real-world loss. Against that, the post is unusually disciplined: it labels its JSON as a pagination excerpt, lists what the verifier does not implement, presents the page cap as an application guard, and declines to recommend replacing a well-tested SDK paginator - which keeps the gap small.
Author promoting own sample repo
The visible incentive is ordinary developer-blog self-promotion: the author showcases a personal .NET 10 verifier, its repository main branch and a merged pull request. No vendor sponsorship, commercial product, pricing or funding interest is disclosed or implied anywhere in the supplied source, and the technical advice runs against self-interest in one place by discouraging hand-rolled paginators in favour of a well-tested SDK.
Single publisher, verifiable mechanism
Confidence is held down by the one-source, one-publisher cluster and by reliance on the author's reading of the specification, and held up by the deterministic, dependency-free nature of the demonstration, which a reader can re-run without an MCP server or credentials. The core mechanism is highly plausible and internally consistent; its real-world frequency and impact are unmeasured.
build
MCP 2026-07-28 drops the `result` wrapper, and your unit tests will not notice1 distinct publisher
build
Wrap OAuth around all of MCP and the directories report zero tools1 distinct publisher
build
Rate limit your MCP servers, because a retrying agent turns one error into a billing incident1 distinct publisher
build
The MCP transport your search results teach has been deprecated since March1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 24, 2026