Repository navigation
Progress notifications not delivered via SSE in stateless HTTP mode #2001
Copy link
Copy link
Closed
Labels
P2Moderate issues affecting some users, edge cases, potentially valuable featureModerate issues affecting some users, edge cases, potentially valuable featurebugSomething isn't workingSomething isn't workingready for workEnough information for someone to start working onEnough information for someone to start working on
Description
Initial Checks
- I confirm that I'm using the latest version of MCP Python SDK
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Description
Summary
Context.report_progress() notifications are not delivered to clients when using the streamable HTTP transport, while Context.info() and other logging methods work correctly.
Root Cause
In src/mcp/server/fastmcp/server.py, the report_progress method does not pass related_request_id to send_progress_notification():
async def report_progress(self, progress: float, total: float | None = None, message: str | None = None) -> None:
progress_token = self.request_context.meta.progressToken if self.request_context.meta else None
if progress_token is None:
return
await self.request_context.session.send_progress_notification(
progress_token=progress_token,
progress=progress,
total=total,
message=message,
# related_request_id is missing here
)The SSE transport uses related_request_id in the message metadata to route notifications to the correct client. Without it, progress notifications are silently dropped.
In contrast, send_log_message (used by info(), warning(), etc.) correctly passes related_request_id:
Fix
Add related_request_id=self.request_id to the send_progress_notification() call:
await self.request_context.session.send_progress_notification(
progress_token=progress_token,
progress=progress,
total=total,
message=message,
related_request_id=self.request_id,
)Reproduction
- Create a FastMCP server with
stateless_http=True - In a tool, call both
await ctx.info("test")andawait ctx.report_progress(0.5, total=1.0, message="test") - Observe that
info()messages are delivered via SSE butreport_progress()messages are not
Environment
- MCP SDK version: 1.26.0
- Python version: 3.11
- Transport: Streamable HTTP (
stateless_http=True)
Example Code
Python & MCP Python SDK
- MCP SDK version: 1.26.0
- Python version: 3.11
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P2Moderate issues affecting some users, edge cases, potentially valuable featureModerate issues affecting some users, edge cases, potentially valuable featurebugSomething isn't workingSomething isn't workingready for workEnough information for someone to start working onEnough information for someone to start working on
Activity
fix: pass related_request_id in Context.report_progress()
koriyoshi2041 commentedon May 30, 2026
I checked this against current main (
616476f) and the originally described missingrelated_request_idpath appears to be fixed now.Evidence:
Context.report_progress()insrc/mcp/server/mcpserver/context.pynow passesrelated_request_id=self.request_idtosend_progress_notification().uv run pytest tests/server/mcpserver/test_server.py::test_report_progress_passes_related_request_id -qconnect_over_streamable_http(..., stateless_http=True): a tool callingctx.report_progress(1, 3)andctx.report_progress(2, 3, "halfway")delivered both progress callbacks before returning.Unless there is a remaining case beyond stateless streamable HTTP progress callback delivery, this may be closable.
shivamsingh-007 commentedon Jul 12, 2026
Confirmed via independent analysis. On \main\ (v2), the code path was completely refactored — \Context.report_progress()\ now routes through _request_outbound.progress()\ (_SingleExchangeDispatchContext), which is inherently request-scoped via its own SSE sink (\src/mcp/server/_streamable_http_modern.py:113-121). No
elated_request_id\ needed because the channel is the request's own channel. All 47 progress-related tests pass across all transport modes including \streamable-http-stateless. Agree this is closable.
shivamsingh-007 commentedon Jul 12, 2026
Apologies for the comment noise on this issue. The earlier comments were redundant — #2001 is already resolved (confirmed on main at 616476f, with a test at #2747). No further action needed.
kuotunyu commentedon Jul 25, 2026
v1.xhas the same fix now: #2994 (commit9678a3b3, merged 2026-06-26) addsrelated_request_id=self.request_idtoContext.report_progress()insrc/mcp/server/fastmcp/server.py, and updatestests/issues/test_176_progress_token.pyto assert it. The comments above covermain; thev1.xside does not seem to have been noted here yet.The part that may still matter for this issue: the fix is not in any published 1.x release.
v1.28.1...v1.xis currently ahead by 5 commits, and9678a3b3is one of them.mcprelease on PyPI.Checked against the released source rather than the branch: at tags
v1.28.0andv1.28.1,report_progress()callssend_progress_notification()withoutrelated_request_id. So anmcp>=1.28.1,<2.0constraint still resolves to the pre-fix behavior.For anyone landing here on 1.28.x, the workaround is to bypass
ctx.report_progress()and pass the field the fix adds:Confirmed on 1.28.1 with the official client SDK's
progress_callbackover both stdio and Streamable HTTP.Is a 1.x patch release including #2994 planned? That would let downstream consumers drop the workaround instead of pinning to a git ref.
Separately: #2207 targets
v1.xwith the same change and is still open, so it looks superseded by #2994.maxisbey commentedon Jul 29, 2026
Thanks for the report. Closing this as it's been fixed in v2, which is now released (see #2038), and as noted above the same fix landed on the 1.x line via #2994. If you still run into this on v2, feel free to reopen.
AI Disclaimer