From 6459c61cce511e9364586637b95f8e277b724ab8 Mon Sep 17 00:00:00 2001 From: OiPunk Date: Thu, 5 Mar 2026 01:41:44 +0800 Subject: [PATCH] fix: pass related_request_id in Context.report_progress() Progress notifications sent via Context.report_progress() were silently dropped in stateless HTTP / SSE transports because the call to send_progress_notification() was missing the related_request_id parameter. The SSE transport relies on this field to route notifications back to the correct client stream. Add related_request_id=self.request_id to the send_progress_notification() call, consistent with how send_log_message() already passes it. Reported-by: hubbard-zlee Github-Issue: #2001 --- src/mcp/server/fastmcp/server.py | 1 + tests/issues/test_176_progress_token.py | 12 ++++-- .../test_2001_progress_related_request_id.py | 41 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tests/issues/test_2001_progress_related_request_id.py diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index 7a43bd7cf..7181f7813 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -1177,6 +1177,7 @@ async def report_progress(self, progress: float, total: float | None = None, mes progress=progress, total=total, message=message, + related_request_id=self.request_id, ) async def read_resource(self, uri: str | AnyUrl) -> Iterable[ReadResourceContents]: diff --git a/tests/issues/test_176_progress_token.py b/tests/issues/test_176_progress_token.py index eb5f19d64..a81e9ba18 100644 --- a/tests/issues/test_176_progress_token.py +++ b/tests/issues/test_176_progress_token.py @@ -36,6 +36,12 @@ async def test_progress_token_zero_first_call(): # Verify progress notifications assert mock_session.send_progress_notification.call_count == 3, "All progress notifications should be sent" - mock_session.send_progress_notification.assert_any_call(progress_token=0, progress=0.0, total=10.0, message=None) - mock_session.send_progress_notification.assert_any_call(progress_token=0, progress=5.0, total=10.0, message=None) - mock_session.send_progress_notification.assert_any_call(progress_token=0, progress=10.0, total=10.0, message=None) + mock_session.send_progress_notification.assert_any_call( + progress_token=0, progress=0.0, total=10.0, message=None, related_request_id="test-request" + ) + mock_session.send_progress_notification.assert_any_call( + progress_token=0, progress=5.0, total=10.0, message=None, related_request_id="test-request" + ) + mock_session.send_progress_notification.assert_any_call( + progress_token=0, progress=10.0, total=10.0, message=None, related_request_id="test-request" + ) diff --git a/tests/issues/test_2001_progress_related_request_id.py b/tests/issues/test_2001_progress_related_request_id.py new file mode 100644 index 000000000..1576fc2b7 --- /dev/null +++ b/tests/issues/test_2001_progress_related_request_id.py @@ -0,0 +1,41 @@ +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from mcp.server.fastmcp import Context +from mcp.shared.context import RequestContext + +pytestmark = pytest.mark.anyio + + +async def test_report_progress_passes_related_request_id(): + """Test that Context.report_progress() passes related_request_id to + send_progress_notification so that progress notifications are correctly + routed in stateless HTTP / SSE transports. + + Regression test for https://github.com/modelcontextprotocol/python-sdk/issues/2001 + """ + mock_session = AsyncMock() + mock_session.send_progress_notification = AsyncMock() + + mock_meta = MagicMock() + mock_meta.progressToken = "test-progress-token" + + request_context = RequestContext( + request_id="req-42", + session=mock_session, + meta=mock_meta, + lifespan_context=None, + ) + + ctx = Context(request_context=request_context, fastmcp=MagicMock()) + + await ctx.report_progress(0.5, total=1.0, message="halfway") + + mock_session.send_progress_notification.assert_called_once_with( + progress_token="test-progress-token", + progress=0.5, + total=1.0, + message="halfway", + related_request_id="req-42", + )