From 76325b7846d6ff25d153fd33205acd50f3a01c70 Mon Sep 17 00:00:00 2001 From: Thomas Sheffler Date: Mon, 27 Jan 2025 19:04:20 -0800 Subject: [PATCH 1/3] progress_token is 0 on first tool-call and the return is taken mistakenly --- src/mcp/server/fastmcp/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index 45f17914a..5c819df32 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -599,7 +599,7 @@ async def report_progress( else None ) - if not progress_token: + if progress_token == None: return await self.request_context.session.send_progress_notification( From 2dc5fbfa3ddb076da33535b83d68f51f2bc43bbb Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 28 Jan 2025 11:19:13 +0000 Subject: [PATCH 2/3] fix: add test for #176 --- tests/issues/test_176_progress_token.py | 46 +++++++++++++++++++++++++ tests/shared/test_sse.py | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/issues/test_176_progress_token.py diff --git a/tests/issues/test_176_progress_token.py b/tests/issues/test_176_progress_token.py new file mode 100644 index 000000000..ed8ab128a --- /dev/null +++ b/tests/issues/test_176_progress_token.py @@ -0,0 +1,46 @@ +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_progress_token_zero_first_call(): + """Test that progress notifications work when progress_token is 0 on first call.""" + + # Create mock session with progress notification tracking + mock_session = AsyncMock() + mock_session.send_progress_notification = AsyncMock() + + # Create request context with progress token 0 + mock_meta = MagicMock() + mock_meta.progressToken = 0 # This is the key test case - token is 0 + + request_context = RequestContext( + request_id="test-request", session=mock_session, meta=mock_meta + ) + + # Create context with our mocks + ctx = Context(request_context=request_context, fastmcp=MagicMock()) + + # Test progress reporting + await ctx.report_progress(0, 10) # First call with 0 + await ctx.report_progress(5, 10) # Middle progress + await ctx.report_progress(10, 10) # Complete + + # 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 + ) + mock_session.send_progress_notification.assert_any_call( + progress_token=0, progress=5.0, total=10.0 + ) + mock_session.send_progress_notification.assert_any_call( + progress_token=0, progress=10.0, total=10.0 + ) diff --git a/tests/shared/test_sse.py b/tests/shared/test_sse.py index 9d32fff34..a229cb1a3 100644 --- a/tests/shared/test_sse.py +++ b/tests/shared/test_sse.py @@ -164,6 +164,7 @@ async def http_client(server, server_url) -> AsyncGenerator[httpx.AsyncClient, N async def test_raw_sse_connection(http_client: httpx.AsyncClient) -> None: """Test the SSE connection establishment simply with an HTTP client.""" async with anyio.create_task_group(): + async def connection_test() -> None: async with http_client.stream("GET", "/sse") as response: assert response.status_code == 200 @@ -211,7 +212,6 @@ async def initialized_sse_client_session( yield session - @pytest.mark.anyio async def test_sse_client_happy_request_and_response( initialized_sse_client_session: ClientSession, From 53bfac0cc2dbd3d757544c542d7f9cf26a53ab4c Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 29 Jan 2025 10:01:56 +0000 Subject: [PATCH 3/3] fix: make progress_token check E711 compat --- src/mcp/server/fastmcp/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index 5c819df32..46977c46c 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -599,7 +599,7 @@ async def report_progress( else None ) - if progress_token == None: + if progress_token is None: return await self.request_context.session.send_progress_notification(