Python: [Bug]: A non-positive MCP request timeout silently disables the timeout
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 82/100
Hướng nghiên cứu
Đọc python/packages/core/agent_framework/_mcp.py, đặc biệt là MCPTool.__init__ gần phần xác thực max_host_payload_size_bytes và xử lý request_timeout quanh dòng 2007. Sau đó, kiểm tra python/packages/core/tests/core/test_mcp.py và chạy bài kiểm thử xác thực constructor được đề xuất. Công việc hoàn tất khi các giá trị không dương gây ra ValueError, còn None vẫn hợp lệ và các timeout dương vẫn được chuyển đến ClientSession.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Observed Behavior
MCPTool accepts a non-positive request_timeout without complaint, and the only place that value is read treats it as "not set", so the request timeout is switched off:
# python/packages/core/agent_framework/_mcp.py:2007 (main @ 726dcff8)
read_timeout_seconds=(
timedelta(seconds=self.request_timeout) if self.request_timeout else None
),
With request_timeout=0, if self.request_timeout is falsy, so read_timeout_seconds=None is passed to ClientSession. In the MCP SDK that is the "never time out" setting, not the strictest one — mcp/shared/session.py:192 documents the parameter as # If none, reading will never time out and :284-291 feeds it to anyio.fail_after(timeout), which is a no-op for None.
So 0 — the value a caller reaches for when they want the tightest possible bound — produces the exact opposite: an unbounded wait. A negative value (request_timeout=-5) is likewise accepted and also lands in the same None branch.
This is not confined to the base class: MCPStdioTool, MCPStreamableHTTPTool and MCPWebsocketTool all forward their own request_timeout argument into MCPTool.__init__ unchanged (:3684, :3963, :4497), so every transport inherits the same behavior.
None is already the documented way to ask for no timeout ("Timeout in seconds for MCP requests", :953), so the 0 path has no useful meaning to preserve.
Expected Behavior
A non-positive request_timeout should be rejected at construction time, the same way the sibling knob right next to it already is:
# python/packages/core/agent_framework/_mcp.py:999
if max_host_payload_size_bytes is not None and max_host_payload_size_bytes <= 0:
raise ValueError("max_host_payload_size_bytes must be positive or None.")
Failing fast also keeps the None meaning intact: callers who genuinely want unbounded requests pass None explicitly instead of discovering, in production, that 0 silently did that for them.
The practical impact is a hang rather than a wrong answer: an agent wired to an MCP server that accepts the connection but never answers a tools/call blocks forever on that request, with no timeout error to catch and no retry to fall back on.
Steps to Reproduce
- Install from
main(726dcff8):agent-framework-core1.21.0 with the repo's ownmcp>=1.24.0,<2pin (1.30.0 here). - Construct a wrapper with an explicit zero timeout — no error is raised:
MCPStdioTool(name="x", command="server", request_timeout=0). - Call
connect()and inspect the keyword argument passed toClientSession:read_timeout_secondsisNone. - Point it at a server that never replies to a tool call; the
call_toolawait never raisesMcpErrorand never returns.
Minimal Reproduction
Both reproduce with the MCP test module's existing harness, no server or API keys needed. Adding these two tests to python/packages/core/tests/core/test_mcp.py reproduces the defect on main:
def test_mcp_request_timeout_must_be_positive_or_none() -> None:
with pytest.raises(ValueError, match="positive or None"):
MCPTool(name="invalid", request_timeout=0) # type: ignore[abstract]
with pytest.raises(ValueError, match="positive or None"):
MCPStdioTool(name="invalid", command="test-command", request_timeout=-1)
async def test_connect_passes_positive_request_timeout_to_session() -> None:
tool = MCPStdioTool(name="test", command="test-command", request_timeout=5, load_tools=False, load_prompts=False)
mock_transport = (Mock(), Mock())
mock_context_manager = Mock()
mock_context_manager.__aenter__ = AsyncMock(return_value=mock_transport)
mock_context_manager.__aexit__ = AsyncMock(return_value=None)
tool.get_mcp_client = Mock(return_value=mock_context_manager) # type: ignore[method-assign]
with patch("mcp.client.session.ClientSession") as mock_session_class:
mock_session = AsyncMock()
mock_session._request_id = 1
session_cm = AsyncMock()
session_cm.__aenter__ = AsyncMock(return_value=mock_session)
session_cm.__aexit__ = AsyncMock(return_value=None)
mock_session_class.return_value = session_cm
await tool.connect()
assert mock_session_class.call_args.kwargs["read_timeout_seconds"] == timedelta(seconds=5)
The first test fails on main; the second already passes and pins the positive-value path so the fix cannot regress it.
Error Messages and Stack Traces
Measured on main (726dcff8), the first test fails and nothing is raised:
> with pytest.raises(ValueError, match="positive or None"):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E Failed: DID NOT RAISE ValueError
tests\core\test_mcp.py:1055: Failed
FAILED tests/core/test_mcp.py::test_mcp_request_timeout_must_be_positive_or_none
Package Versions
agent-framework-core: 1.21.0 (source main @ 726dcff8); mcp: 1.30.0 (repo pin mcp>=1.24.0,<2)
Python Version
Python 3.12.11
Operating System
Windows
- Ngôn ngữ chính
- Python
- Star
- 14k
- Fork
- 2.5k
- Merge trung bình
- 2 ngày 7 giờ
- Pull request đã merge (30 ngày)
- 416
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc 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 microsoft/agent-framework
-
Python: [Bug]: Out-of-range Cosmos memory search parameters silently disable retrievalCó thể đã có người làm @eavanvalkenburg đã nhận 2 ngày trước. Đang mởpython
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
microsoft/agent-framework#9317 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Python: [Bug]: Content.from_dict leaves the nested content of image generation and MCP server tool results as plain dictsCó thể đã có người làm @eavanvalkenburg đã nhận 2 ngày trước. Đang mởpython
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
microsoft/agent-framework#9303 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
.NET: Proposal: add an llms.txtĐang mở.NET python triage
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
microsoft/agent-framework#9092 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Python: raw-data content mappings lose annotations and attachment metadataCó thể đã có người làm @moonbox3 đã nhận 15 ngày trước. Đang mởpython triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
microsoft/agent-framework#8632 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Python: Clarify when to use platformCó thể đã có người làm @eavanvalkenburg đã nhận 15 ngày trước. Đang mởpython triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
microsoft/agent-framework#8599 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của microsoft/agent-framework
Issue tương tự
-
Path subtraction removes repeated suffix characters instead of one suffixCó thể đã có người làm @Gwerdonatus đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
joke2k/django-environ#671 ·
-
good first issue help wanted windows
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 80/100
-
test_pgbouncer deploys the server under test from Charmhub instead of the local build (track/1.31)Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
canonical/temporal-k8s-operator#195 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 4 ngày
-
`burr` fails to start when the Python path has a space in itCó thể đã có người làm @enekoreto đã nhận hôm nay. Đang mởstatus/needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 5 ngày
-
device support request
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
zigpy/zha-device-handlers#5461 ·
Maintainer thường phản hồi trong vòng 4 ngày