Add list_all_* helpers that drain pagination on the client#3021
Open
CJGjr wants to merge 6 commits into
Open
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
This was referenced Jun 29, 2026
Currently Client.list_tools / list_prompts / list_resources / list_resource_templates return a single page and the caller has to loop on next_cursor manually. Add list_all_tools / list_all_prompts / list_all_resources / list_all_resource_templates that walk next_cursor until exhausted, plus iter_all_* async iterators for streaming consumers. The single-page methods get a docstring update pointing at the new drains. ClientSessionGroup switches its tool/prompt/resource aggregation to the drain helper so its consumers always see the full collection across multi-page servers. Implements the helper maxisbey endorsed in modelcontextprotocol#2556. Rebased onto the v2 rework: types import from mcp_types, the stream-spy tests run in legacy mode, and the test Tool carries a valid input_schema.
A server that returns the same next_cursor it was given would make the list_all_*/iter_all_* loops page forever. Raise RuntimeError when the cursor does not advance instead of silently looping or truncating, and document it in the Raises section of each helper. Covered by a parametrized test across tools, prompts, resources, and templates.
Add a Draining in one call section to docs/advanced/pagination.md covering list_all_*/iter_all_*, the ClientSessionGroup behavior, and the non-advancing cursor guard. Backed by a runnable tutorial003 snippet and matching tests, in keeping with the page proving every claim.
The pagination guide says every drain fails loudly when a server returns the same cursor it was handed, but the group aggregator's drain loop had no such guard and would page forever. Guard it the same way the Client drains are guarded.
CJGjr
force-pushed
the
list-all-pagination-v2
branch
from
July 21, 2026 18:54
15d146f to
36a8bdd
Compare
The page lives at client/session-groups.md; the relative link broke when the guide moved under docs/advanced/ during the rebase, and zensical build --strict rejects it.
Comparing only against the immediately preceding cursor catches a server that echoes the cursor back but not one that alternates between cursors (a, b, a, ...), which would still page forever. Track every cursor seen during the drain and raise on any repeat, in both the Client drains and the ClientSessionGroup aggregation drain.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2556.
This continues #2658 by @adityasingh2400, which implemented the helpers but went stale against the v2 rework on
main. I opened the original issue, so I rebased the work onto currentmain, added a guard, and am putting it up as a ready-to-merge PR. Aditya's authorship is preserved on the feature commit. Happy to close this in favor of an updated #2658 if he would rather carry it forward.What this adds
list_all_tools/list_all_prompts/list_all_resources/list_all_resource_templatesonClient: each walksnext_cursoruntil the server reports no more pages and returns the combined list.iter_all_tools/iter_all_prompts/iter_all_resources/iter_all_resource_templates: async iterators for streaming consumers that do not want to materialize every page.list_*methods get docstrings pointing at the new drains.ClientSessionGroupaggregation drains pagination, so multi-server consumers see the full collection instead of only page 0.The single-page primitives are unchanged; the drains are opt-in.
Non-advancing cursor guard
A server that keeps returning the same cursor it was handed would make a naive drain loop page forever. The loops raise
RuntimeErrorwhen the cursor does not advance, rather than looping or silently truncating (silent truncation being the exact failure the issue describes). Documented in each helper'sRaises:section and covered by a parametrized test across tools, prompts, resources, and templates. This addresses the malformed-server concern @agaonker raised on the issue. Easy to switch to a quiet stop instead if that is preferred.Notes on the v2 rebase
The original PR predated the v2 rework. Bringing it current meant: types import from
mcp_types, the cursor-spy tests run withmode="legacy"(the default client path is now streamless, so the wire spy has nothing to observe otherwise), and the testToolcarries a validinput_schemanow thattypeis required. The helper logic itself is unchanged.Docs
docs/advanced/pagination.mdgets a "Draining in one call" section coveringlist_all_*/iter_all_*, theClientSessionGroupbehavior, and the cursor guard. It is backed by a runnabledocs_src/pagination/tutorial003.pysnippet with matching tests, so the page keeps proving every claim against the SDK.Verification
uv run pytest tests/client/andtests/docs_src/test_pagination.pypassuv run pyright src/mcp/client/client.py src/mcp/client/session_group.pycleanuv run ruff check . && uv run ruff format --check .cleanreadme-snippetsandmarkdownlint(repo config) cleanAI disclosure: developed with AI assistance (Claude, Fable 5). I have read and tested every change and can speak to all of it myself.