Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Python: [Bug]: A non-positive MCP request timeout silently disables the timeout

Đang mở Phù hợp với người mới
#9,313 0 bình luận 0 reaction 1 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@jpalvarezl đang làm issue này rồi.

Từ ngày 13/10/2026.

  • #9314 của @Lesereingrape — đang mở

Đá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
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
ai, backend

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ả

python
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
  1. Install from main (726dcff8): agent-framework-core 1.21.0 with the repo's own mcp>=1.24.0,<2 pin (1.30.0 here).
  2. Construct a wrapper with an explicit zero timeout — no error is raised: MCPStdioTool(name="x", command="server", request_timeout=0).
  3. Call connect() and inspect the keyword argument passed to ClientSession: read_timeout_seconds is None.
  4. Point it at a server that never replies to a tool call; the call_tool await never raises McpError and 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

Mở trong Codespaces

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.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. 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.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của microsoft/agent-framework

Tất cả issue của microsoft/agent-framework

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.