You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ClientSessionGroup silently omits later pages of tools, resources, and prompts #3792
ClientSessionGroup._aggregate_components() calls each list_*() method once and ignores next_cursor. When a server paginates its catalog, the group registers only the first page.
This leaves later-page tools unavailable through group.call_tool(), even though the connected server exposes them.
Reproduction
Use an in-process server whose tools listing returns:
Tool first, with next_cursor="page-2".
Tool second, with no continuation cursor.
Connect its session using group.connect_with_session(...).
Observed:group.tools contains only first, and the server receives one listing request.
Expected: the group follows the cursor and registers both tools. Resources and prompts have the same single-page aggregation behavior.
Reproduced on main at d7845c07.
Proposed fix
Drain pagination within group aggregation while preserving naming hooks, ownership routing, and collision checks.
Stage the complete catalog before registering components, so a continuation-page failure does not publish a partial catalog. Detect repeated cursors to avoid an endless listing loop, and treat cursor values as opaque—including an empty string.
This is narrower than #2556’s proposal for new public list_all_* helpers: it fixes the existing group’s aggregation behavior without adding a public API.
Hi, we are TechAurah. We would like to work on this if a maintainer is willing to assign it to us.
Our read of the problem: ClientSessionGroup fetches only the first page when listing each server's tools, resources, and prompts, so components behind a nextCursor are never registered and cannot be called through the group. The fix drains each catalog by following cursors until none remains, for all three component types, before registration.
We noticed the earlier attempt (#3793) was auto-closed because the author was not assigned to this issue, which is why we are asking here first. We will include pagination tests with multi-page fake servers for tools, resources, and prompts.
Happy to adjust the approach if the maintainers prefer a different shape.
Confirmed on main, and the fix shape you propose matches the code's existing intent: _aggregate_components at src/mcp/client/session_group.py:374 already builds prompts_temp, resources_temp and tools_temp and only merges on success, so a staged drain fits rather than fights the current structure.
Three details where the drain can go wrong:
Cycle detection needs a visited set, not a comparison with the previous cursor. A server that alternates page-a, page-b, page-a never returns the same cursor twice in a row and loops forever under a last-cursor check. A page cap as well as a visited set covers the other case, a server that mints a fresh cursor per page and never terminates.
Treat nextCursor of "" as terminal, and treat any non-string as terminal. The field is optional in the schema, but empty-string cursors are common in the wild and a while cursor: loop and a while cursor is not None: loop disagree about them. That difference decides whether a server that ends with "" costs one extra listing round trip or hangs on a repeat.
Collision detection has to run on the drained catalog. The temp dicts are keyed by the final component name, so a later-page tool whose name matches an earlier-page one silently overwrites it instead of raising the collision the group reports elsewhere. Draining first and checking names after makes that case visible.
One thing the change will not reach: _aggregate_components calls only list_prompts, list_resources and list_tools (:391, :401, :411). There is no list_resource_templates call, so resource templates are not aggregated by the group at all, paginated or not. Worth stating in the PR description so the pagination fix is not read as full catalog coverage.
Last consideration: connect_with_session aggregates for every new session, so a server that paginates slowly now delays group startup by the number of pages. A per-page timeout, rather than one budget for the whole drain, keeps a single slow page from failing a connection that would otherwise work.
Thanks @media-ctrl, this is exactly the kind of review that makes the fix safer. Confirming on our side too, and we will build the drain with your points in from the start: a visited set plus a page cap for cycle detection, empty string and non-string cursors treated as terminal, collision detection run on the fully drained catalog, and a per-page timeout rather than one budget for the whole drain. The PR description will also state plainly that resource templates are not aggregated by the group today, so the pagination fix is not read as full catalog coverage.
We are ready to implement this. Could a maintainer assign this issue to @techaurah? Our earlier PR attempt was auto-closed because we were not assigned, so we would rather open the PR once it can land.
Activity
techaurah commentedon Nov 1, 2026
Hi, we are TechAurah. We would like to work on this if a maintainer is willing to assign it to us.
Our read of the problem: ClientSessionGroup fetches only the first page when listing each server's tools, resources, and prompts, so components behind a nextCursor are never registered and cannot be called through the group. The fix drains each catalog by following cursors until none remains, for all three component types, before registration.
We noticed the earlier attempt (#3793) was auto-closed because the author was not assigned to this issue, which is why we are asking here first. We will include pagination tests with multi-page fake servers for tools, resources, and prompts.
Happy to adjust the approach if the maintainers prefer a different shape.
media-ctrl commentedon Nov 4, 2026
Confirmed on main, and the fix shape you propose matches the code's existing intent:
_aggregate_componentsatsrc/mcp/client/session_group.py:374already buildsprompts_temp,resources_tempandtools_tempand only merges on success, so a staged drain fits rather than fights the current structure.Three details where the drain can go wrong:
page-a,page-b,page-anever returns the same cursor twice in a row and loops forever under a last-cursor check. A page cap as well as a visited set covers the other case, a server that mints a fresh cursor per page and never terminates.nextCursorof""as terminal, and treat any non-string as terminal. The field is optional in the schema, but empty-string cursors are common in the wild and awhile cursor:loop and awhile cursor is not None:loop disagree about them. That difference decides whether a server that ends with""costs one extra listing round trip or hangs on a repeat.One thing the change will not reach:
_aggregate_componentscalls onlylist_prompts,list_resourcesandlist_tools(:391,:401,:411). There is nolist_resource_templatescall, so resource templates are not aggregated by the group at all, paginated or not. Worth stating in the PR description so the pagination fix is not read as full catalog coverage.Last consideration:
connect_with_sessionaggregates for every new session, so a server that paginates slowly now delays group startup by the number of pages. A per-page timeout, rather than one budget for the whole drain, keeps a single slow page from failing a connection that would otherwise work.techaurah commentedon Nov 4, 2026
Thanks @media-ctrl, this is exactly the kind of review that makes the fix safer. Confirming on our side too, and we will build the drain with your points in from the start: a visited set plus a page cap for cycle detection, empty string and non-string cursors treated as terminal, collision detection run on the fully drained catalog, and a per-page timeout rather than one budget for the whole drain. The PR description will also state plainly that resource templates are not aggregated by the group today, so the pagination fix is not read as full catalog coverage.
We are ready to implement this. Could a maintainer assign this issue to @techaurah? Our earlier PR attempt was auto-closed because we were not assigned, so we would rather open the PR once it can land.