ClientSessionGroup raises KeyError when a server exposes no tools, resources or prompts
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 70/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- python
- Lĩnh vực
- backend-api-design
Hướng nghiên cứu
Bắt đầu trong mcp/client/session_group.py tại _aggregate_components, khoảng dòng 420, sau đó chạy ví dụ máy chủ trống trong issue qua cả connect_with_session() và connect_to_server(). Kiểm tra các đường dẫn dọn dẹp session hiện có và disconnect_from_server(); hoàn tất khi các máy chủ trống không còn gây ra KeyError và mọi session stack bị loại bỏ đều được đóng đúng cách, với coverage cho cả hai đường dẫn kết nối.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Initial Checks
- I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Release line
2.x (current stable), and it reproduces on the latest 1.x too.
Description
Connecting a ClientSessionGroup to a server that registers nothing fails with a raw KeyError. A server that registers even one tool, resource or prompt is fine.
_aggregate_components ends with this cleanup step, which only runs when all three list calls came back empty (session_group.py:420 on 2.1.0):
# Clean up exit stack for session if we couldn't retrieve anything
# from the server.
if not any((prompts_temp, resources_temp, tools_temp)):
del self._session_exit_stacks[session] # pragma: no cover
Two different things go wrong depending on how you got there.
Via connect_with_session(), the key was never inserted, so the del raises KeyError. That is the traceback below.
Via connect_to_server(), the key does exist, so the del succeeds. It removes the bookkeeping entry without closing the stack. The next lines then register the session in self._sessions anyway, so the group reports the session as connected while holding no way to close it. Calling disconnect_from_server() afterwards takes the session_known_for_stack is False branch and returns without closing anything, so the transport stays open until the whole group is torn down. I confirmed this by pre-registering a stack with a close callback and watching the callback never fire.
What I expected: connecting to a server that exposes nothing should either succeed with an empty component set, or fail with a clear error. If the group decides to drop the session, the exit stack should be closed rather than forgotten.
Two smaller things in the same condition, for whoever picks this up. The comment says "clean up exit stack" but the code only forgets it. And not any(...) treats "the server legitimately exposes nothing" and "all three list calls raised" as the same case, which may deserve separating.
Example Code
import anyio
import mcp_types as types
from mcp.client.client import Client
from mcp.client.session_group import ClientSessionGroup
from mcp.server.mcpserver import MCPServer
INFO = types.Implementation(name="empty", version="1.0")
async def main() -> None:
# A server that registers no tools, no resources and no prompts.
async with Client(MCPServer("empty")) as client:
group = ClientSessionGroup()
await group.connect_with_session(INFO, client.session)
anyio.run(main)
Output:
File ".../mcp/client/session_group.py", line 420, in _aggregate_components
del self._session_exit_stacks[session] # pragma: no cover
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
KeyError: <mcp.client.session.ClientSession object at 0x77e8450e16a0>
Python & MCP Python SDK
Python 3.13.11, Linux.
mcp 2.1.0 (latest 2.x): fails at session_group.py:420
mcp 1.29.1 (latest 1.x): fails at session_group.py:409
Also reproduces on main at d8b63830.
Reported with AI assistance; I ran the reproduction above and read the output.
- Ngôn ngữ chính
- Python
- Star
- 24.3k
- Fork
- 4k
- Merge trung bình
- 1 ngày 19 phút
- Pull request đã merge (30 ngày)
- 29
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của modelcontextprotocol/python-sdk
-
Streamable HTTP client logs a WARNING for valid 202 Accepted on session termination (DELETE) Đang mởv1 v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
modelcontextprotocol/python-sdk#3546 · 5 bình luận ·
-
v1 v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
modelcontextprotocol/python-sdk#3545 · 1 bình luận ·
-
v1 v2
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 91/100
modelcontextprotocol/python-sdk#3508 · 2 bình luận ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 64/100
modelcontextprotocol/python-sdk#3504 ·
-
v1 v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
modelcontextprotocol/python-sdk#3492 · 1 bình luận ·
Tất cả issue của modelcontextprotocol/python-sdk
Issue tương tự
-
from:qa priority:P2 reliability tech-debt
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
spec-kitty/spec-kitty#4874 ·
-
fix: inaccuracy ⚠️
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
uabrc/uabrc.github.io#1255 · 1 bình luận ·
-
kind:bug needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
-
docs
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
ethereum-optimism/factory#64 ·