refactor: remove unused mcp.shared.progress module (#2080)

This commit is contained in:
Max Isbey 2026-02-18 13:10:02 +00:00 • committed by GitHub
commit e82203bfc4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 32 additions and 171 deletions

View file

@ -371,7 +371,7 @@ async def handle_call_tool(ctx: ServerRequestContext, params: CallToolRequestPar
server = Server("my-server", on_call_tool=handle_call_tool) server = Server("my-server", on_call_tool=handle_call_tool)
``` ```
### `RequestContext` and `ProgressContext` type parameters simplified ### `RequestContext` type parameters simplified
The `RequestContext` class has been split to separate shared fields from server-specific fields. The shared `RequestContext` now only takes 1 type parameter (the session type) instead of 3. The `RequestContext` class has been split to separate shared fields from server-specific fields. The shared `RequestContext` now only takes 1 type parameter (the session type) instead of 3.
@ -380,40 +380,59 @@ The `RequestContext` class has been split to separate shared fields from server-
- Type parameters reduced from `RequestContext[SessionT, LifespanContextT, RequestT]` to `RequestContext[SessionT]` - Type parameters reduced from `RequestContext[SessionT, LifespanContextT, RequestT]` to `RequestContext[SessionT]`
- Server-specific fields (`lifespan_context`, `experimental`, `request`, `close_sse_stream`, `close_standalone_sse_stream`) moved to new `ServerRequestContext` class in `mcp.server.context` - Server-specific fields (`lifespan_context`, `experimental`, `request`, `close_sse_stream`, `close_standalone_sse_stream`) moved to new `ServerRequestContext` class in `mcp.server.context`
**`ProgressContext` changes:**
- Type parameters reduced from `ProgressContext[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT]` to `ProgressContext[SessionT]`
**Before (v1):** **Before (v1):**
```python ```python
from mcp.client.session import ClientSession from mcp.client.session import ClientSession
from mcp.shared.context import RequestContext, LifespanContextT, RequestT from mcp.shared.context import RequestContext, LifespanContextT, RequestT
from mcp.shared.progress import ProgressContext
# RequestContext with 3 type parameters # RequestContext with 3 type parameters
ctx: RequestContext[ClientSession, LifespanContextT, RequestT] ctx: RequestContext[ClientSession, LifespanContextT, RequestT]
# ProgressContext with 5 type parameters
progress_ctx: ProgressContext[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT]
``` ```
**After (v2):** **After (v2):**
```python ```python
from mcp.client.context import ClientRequestContext from mcp.client.context import ClientRequestContext
from mcp.client.session import ClientSession
from mcp.server.context import ServerRequestContext, LifespanContextT, RequestT from mcp.server.context import ServerRequestContext, LifespanContextT, RequestT
from mcp.shared.progress import ProgressContext
# For client-side context (sampling, elicitation, list_roots callbacks) # For client-side context (sampling, elicitation, list_roots callbacks)
ctx: ClientRequestContext ctx: ClientRequestContext
# For server-specific context with lifespan and request types # For server-specific context with lifespan and request types
server_ctx: ServerRequestContext[LifespanContextT, RequestT] server_ctx: ServerRequestContext[LifespanContextT, RequestT]
```
# ProgressContext with 1 type parameter ### `ProgressContext` and `progress()` context manager removed
progress_ctx: ProgressContext[ClientSession]
The `mcp.shared.progress` module (`ProgressContext`, `Progress`, and the `progress()` context manager) has been removed. This module had no real-world adoption — all users send progress notifications via `Context.report_progress()` or `session.send_progress_notification()` directly.
**Before:**
```python
from mcp.shared.progress import progress
with progress(ctx, total=100) as p:
await p.progress(25)
```
**After — use `Context.report_progress()` (recommended):**
```python
@server.tool()
async def my_tool(x: int, ctx: Context) -> str:
await ctx.report_progress(25, 100)
return "done"
```
**After — use `session.send_progress_notification()` (low-level):**
```python
await session.send_progress_notification(
progress_token=progress_token,
progress=25,
total=100,
)
``` ```
### Resource URI type changed from `AnyUrl` to `str` ### Resource URI type changed from `AnyUrl` to `str`

View file

@ -1,45 +0,0 @@
from collections.abc import Generator
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Generic
from pydantic import BaseModel
from mcp.shared._context import RequestContext, SessionT
from mcp.types import ProgressToken
class Progress(BaseModel):
progress: float
total: float | None
@dataclass
class ProgressContext(Generic[SessionT]):
session: SessionT
progress_token: ProgressToken
total: float | None
current: float = field(default=0.0, init=False)
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, message=message
)
@contextmanager
def progress(
ctx: RequestContext[SessionT],
total: float | None = None,
) -> Generator[ProgressContext[SessionT], None]:
progress_token = ctx.meta.get("progress_token") if ctx.meta else None
if progress_token is None: # pragma: no cover
raise ValueError("No progress token provided")
progress_ctx = ProgressContext(ctx.session, progress_token, total)
try:
yield progress_ctx
finally:
pass

View file

@ -10,9 +10,7 @@ from mcp.server import Server, ServerRequestContext
from mcp.server.lowlevel import NotificationOptions from mcp.server.lowlevel import NotificationOptions
from mcp.server.models import InitializationOptions from mcp.server.models import InitializationOptions
from mcp.server.session import ServerSession from mcp.server.session import ServerSession
from mcp.shared._context import RequestContext
from mcp.shared.message import SessionMessage from mcp.shared.message import SessionMessage
from mcp.shared.progress import progress
from mcp.shared.session import RequestResponder from mcp.shared.session import RequestResponder
@ -198,117 +196,6 @@ async def test_bidirectional_progress_notifications():
assert server_progress_updates[2]["progress"] == 1.0 assert server_progress_updates[2]["progress"] == 1.0
@pytest.mark.anyio
async def test_progress_context_manager():
"""Test client using progress context manager for sending progress notifications."""
# Create memory streams for client/server
server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[SessionMessage](5)
client_to_server_send, client_to_server_receive = anyio.create_memory_object_stream[SessionMessage](5)
# Track progress updates
server_progress_updates: list[dict[str, Any]] = []
progress_token = None
# Register progress handler
async def handle_progress(ctx: ServerRequestContext, params: types.ProgressNotificationParams) -> None:
server_progress_updates.append(
{
"token": params.progress_token,
"progress": params.progress,
"total": params.total,
"message": params.message,
}
)
server = Server(name="ProgressContextTestServer", on_progress=handle_progress)
# Run server session to receive progress updates
async def run_server():
# Create a server session
async with ServerSession(
client_to_server_receive,
server_to_client_send,
InitializationOptions(
server_name="ProgressContextTestServer",
server_version="0.1.0",
capabilities=server.get_capabilities(NotificationOptions(), {}),
),
) as server_session:
async for message in server_session.incoming_messages:
try:
await server._handle_message(message, server_session, {})
except Exception as e: # pragma: no cover
raise e
# Client message handler
async def handle_client_message(
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
) -> None:
if isinstance(message, Exception): # pragma: no cover
raise message
# run client session
async with (
ClientSession(
server_to_client_receive,
client_to_server_send,
message_handler=handle_client_message,
) as client_session,
anyio.create_task_group() as tg,
):
tg.start_soon(run_server)
await client_session.initialize()
progress_token = "client_token_456"
# Create request context
request_context = RequestContext(
request_id="test-request",
session=client_session,
meta={"progress_token": progress_token},
)
# Utilize progress context manager
with progress(request_context, total=100) as p:
await p.progress(10, message="Loading configuration...")
await p.progress(30, message="Connecting to database...")
await p.progress(40, message="Fetching data...")
await p.progress(20, message="Processing results...")
# Wait for all messages to be processed
await anyio.sleep(0.5)
tg.cancel_scope.cancel()
# Verify progress updates were received by server
assert len(server_progress_updates) == 4
# first update
assert server_progress_updates[0]["token"] == progress_token
assert server_progress_updates[0]["progress"] == 10
assert server_progress_updates[0]["total"] == 100
assert server_progress_updates[0]["message"] == "Loading configuration..."
# second update
assert server_progress_updates[1]["token"] == progress_token
assert server_progress_updates[1]["progress"] == 40
assert server_progress_updates[1]["total"] == 100
assert server_progress_updates[1]["message"] == "Connecting to database..."
# third update
assert server_progress_updates[2]["token"] == progress_token
assert server_progress_updates[2]["progress"] == 80
assert server_progress_updates[2]["total"] == 100
assert server_progress_updates[2]["message"] == "Fetching data..."
# final update
assert server_progress_updates[3]["token"] == progress_token
assert server_progress_updates[3]["progress"] == 100
assert server_progress_updates[3]["total"] == 100
assert server_progress_updates[3]["message"] == "Processing results..."
@pytest.mark.anyio @pytest.mark.anyio
async def test_progress_callback_exception_logging(): async def test_progress_callback_exception_logging():
"""Test that exceptions in progress callbacks are logged and \ """Test that exceptions in progress callbacks are logged and \