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 leaves old tools after disconnecting a re-registered session #3713
Registering the same caller-owned session twice can leave tools behind after disconnect. The first catalog contains old; the second contains only new. Both connect_with_session() calls return successfully. After disconnect_from_server(), group.sessions == [] but group.tools still contains old.
I expected disconnect to remove every tool registered through that session. A deliberate rejection of repeated registration or a coherent refresh would both be reasonable; this report concerns the accepted repeat leaving stale entries.
The aggregation path replaces the session's reverse index with the latest catalog while updating the aggregate dictionary, which retains the earlier entries. Disconnect then removes only the names in the replacement index. This differs from #3490's transport leak after a rejected connection and #3384's empty-catalog failure.
uv run --frozen pytest tests/client/test_session_group.py::test_disconnect_removes_all_tools_after_same_session_reregistration -q
Python & MCP Python SDK
Python 3.14.7, Linux x86_64. Reproduces on latest stable 2.3.0 (2118f14f8a19bc158d8a1cf90af58d85d187f849) and main 754254c08fe1c3eda18b69da3d336106fe8d72d6. The other 12 session-group tests pass against each source revision.
AI-assisted investigation and test reproduction, with a human in the loop.
Hi! @inanna-malick
I'd like to work on this issue. Could you please assign it to me? I'll reproduce it with the provided test case and put up a PR shortly.
I investigated the root cause and verified the reproduction:
In ClientSessionGroup._aggregate_components(), when an existing session is re-registered via connect_with_session(), self._sessions[session] is overwritten with the new catalog's names while self._tools and self._tool_to_session retain the previous entries. On disconnect_from_server(), only the names in the current _sessions[session] index are removed, leaving earlier tools orphaned in group.tools.
I've tested and prepared a coherent refresh approach in #3716:
Evicts the session's previously registered components from _tools, _resources, _prompts, and _tool_to_session before aggregating the new catalog.
Excludes the session's own existing registrations during duplicate collision checks so re-registering does not trigger false collisions against itself, while still guarding against collisions with other sessions.
Guards del self._session_exit_stacks[session] with if session in self._session_exit_stacks: to handle caller-owned empty-catalog registrations safely without KeyError.
Documents this refresh behavior in docs/client/session-groups.md.
Includes comprehensive invariant unit tests in tests/client/test_session_group.py achieving 100% statement and branch coverage on session_group.py.
Would appreciate being assigned to this issue so CI can run and #3716 can be reviewed when capacity allows. Thanks!
Activity
abhay-2108 commentedon Oct 15, 2026
Hi! @inanna-malick
I'd like to work on this issue. Could you please assign it to me? I'll reproduce it with the provided test case and put up a PR shortly.
abhay-2108 commentedon Oct 15, 2026
I investigated the root cause and verified the reproduction:
In
ClientSessionGroup._aggregate_components(), when an existing session is re-registered viaconnect_with_session(),self._sessions[session]is overwritten with the new catalog's names whileself._toolsandself._tool_to_sessionretain the previous entries. Ondisconnect_from_server(), only the names in the current_sessions[session]index are removed, leaving earlier tools orphaned ingroup.tools.I've tested and prepared a coherent refresh approach in #3716:
_tools,_resources,_prompts, and_tool_to_sessionbefore aggregating the new catalog.del self._session_exit_stacks[session]withif session in self._session_exit_stacks:to handle caller-owned empty-catalog registrations safely withoutKeyError.docs/client/session-groups.md.tests/client/test_session_group.pyachieving 100% statement and branch coverage onsession_group.py.Would appreciate being assigned to this issue so CI can run and #3716 can be reviewed when capacity allows. Thanks!