Skip to content

feat: add message to ProgressNotification #435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 13, 2025
Merged
7 changes: 6 additions & 1 deletion src/mcp/client/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,11 @@ async def send_ping(self) -> types.EmptyResult:
)

async def send_progress_notification(
self, progress_token: str | int, progress: float, total: float | None = None
self,
progress_token: str | int,
progress: float,
total: float | None = None,
message: str | None = None,
) -> None:
"""Send a progress notification."""
await self.send_notification(
Expand All @@ -179,6 +183,7 @@ async def send_progress_notification(
progressToken=progress_token,
progress=progress,
total=total,
message=message,
),
),
)
Expand Down
8 changes: 6 additions & 2 deletions src/mcp/server/fastmcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -952,13 +952,14 @@ def request_context(self) -> RequestContext[ServerSessionT, LifespanContextT]:
return self._request_context

async def report_progress(
self, progress: float, total: float | None = None
self, progress: float, total: float | None = None, message: str | None = None
) -> None:
"""Report progress for the current operation.

Args:
progress: Current progress value e.g. 24
total: Optional total value e.g. 100
message: Optional message e.g. Starting render...
"""

progress_token = (
Expand All @@ -971,7 +972,10 @@ async def report_progress(
return

await self.request_context.session.send_progress_notification(
progress_token=progress_token, progress=progress, total=total
progress_token=progress_token,
progress=progress,
total=total,
message=message,
)

async def read_resource(self, uri: str | AnyUrl) -> Iterable[ReadResourceContents]:
Expand Down
12 changes: 9 additions & 3 deletions src/mcp/server/lowlevel/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ async def handle_list_resource_templates() -> list[types.ResourceTemplate]:
3. Define notification handlers if needed:
@server.progress_notification()
async def handle_progress(
progress_token: str | int, progress: float, total: float | None
progress_token: str | int, progress: float, total: float | None,
message: str | None
) -> None:
# Implementation

Expand Down Expand Up @@ -427,13 +428,18 @@ async def handler(req: types.CallToolRequest):

def progress_notification(self):
def decorator(
func: Callable[[str | int, float, float | None], Awaitable[None]],
func: Callable[
[str | int, float, float | None, str | None], Awaitable[None]
],
):
logger.debug("Registering handler for ProgressNotification")

async def handler(req: types.ProgressNotification):
await func(
req.params.progressToken, req.params.progress, req.params.total
req.params.progressToken,
req.params.progress,
req.params.total,
req.params.message,
)

self.notification_handlers[types.ProgressNotification] = handler
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/server/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ async def send_progress_notification(
progress_token: str | int,
progress: float,
total: float | None = None,
message: str | None = None,
related_request_id: str | None = None,
) -> None:
"""Send a progress notification."""
Expand All @@ -293,6 +294,7 @@ async def send_progress_notification(
progressToken=progress_token,
progress=progress,
total=total,
message=message,
),
)
),
Expand Down
4 changes: 2 additions & 2 deletions src/mcp/shared/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ class ProgressContext(
total: float | None
current: float = field(default=0.0, init=False)

async def progress(self, amount: float) -> None:
async def progress(self, amount: float, message: str | None = None) -> None:
self.current += amount

await self.session.send_progress_notification(
self.progress_token, self.current, total=self.total
self.progress_token, self.current, total=self.total, message=message
)


Expand Down
6 changes: 5 additions & 1 deletion src/mcp/shared/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,11 @@ async def _received_notification(self, notification: ReceiveNotificationT) -> No
"""

async def send_progress_notification(
self, progress_token: str | int, progress: float, total: float | None = None
self,
progress_token: str | int,
progress: float,
total: float | None = None,
message: str | None = None,
) -> None:
"""
Sends a progress notification for a request that is currently being
Expand Down
5 changes: 5 additions & 0 deletions src/mcp/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,11 @@ class ProgressNotificationParams(NotificationParams):
total is unknown.
"""
total: float | None = None
"""
Message related to progress. This should provide relevant human readable
progress information.
"""
message: str | None = None
"""Total number of items to process (or total progress required), if known."""
model_config = ConfigDict(extra="allow")

Expand Down
6 changes: 3 additions & 3 deletions tests/issues/test_176_progress_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ async def test_progress_token_zero_first_call():
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
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
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
progress_token=0, progress=10.0, total=10.0, message=None
)
Loading
Loading