Initial Checks
Release line
2.x (current stable)
Description
What I ran into
ClientSessionGroup._aggregate_components deletes the session from
_session_exit_stacks whenever a connected server exposes no prompts, resources,
or tools:
if not any((prompts_temp, resources_temp, tools_temp)):
del self._session_exit_stacks[session]
Via connect_with_session(server_info, session) the caller supplies a session
that was never registered in _session_exit_stacks, so this raises KeyError.
Via connect_to_server it drops the freshly-registered exit stack, so a later
disconnect_from_server can't close that session's transport.
Why it matters
A valid MCP server can legitimately register zero tools/resources/prompts (e.g.
one that only does sampling/roots), and list_* failures are swallowed into
empty dicts too. In both cases connecting an otherwise-fine server either crashes
or leaks a transport until group teardown.
Minimal reproduction
group = ClientSessionGroup()
# server_info + an in-memory session whose list_tools/resources/prompts are empty
await group.connect_with_session(server_info, session) # -> KeyError
Suggested fix
Remove the empty-server cleanup block so an empty server is aggregated like any
other. Happy to open a PR (branch already prepared).
Example Code
Python & MCP Python SDK
Python 3.13.2
MCP Python SDK: main @ f1b6589 (dev build 0.0.1.dev1063)
Initial Checks
Release line
2.x (current stable)
Description
What I ran into
ClientSessionGroup._aggregate_componentsdeletes the session from_session_exit_stackswhenever a connected server exposes no prompts, resources,or tools:
Via
connect_with_session(server_info, session)the caller supplies a sessionthat was never registered in
_session_exit_stacks, so this raisesKeyError.Via
connect_to_serverit drops the freshly-registered exit stack, so a laterdisconnect_from_servercan't close that session's transport.Why it matters
A valid MCP server can legitimately register zero tools/resources/prompts (e.g.
one that only does sampling/roots), and
list_*failures are swallowed intoempty dicts too. In both cases connecting an otherwise-fine server either crashes
or leaks a transport until group teardown.
Minimal reproduction
Suggested fix
Remove the empty-server cleanup block so an empty server is aggregated like any
other. Happy to open a PR (branch already prepared).
Example Code
Python & MCP Python SDK