Skip to content

Commit e82203b

Browse files
authored
refactor: remove unused mcp.shared.progress module (#2080)
1 parent fc57c2c commit e82203b

File tree

3 files changed

+32
-171
lines changed

3 files changed

+32
-171
lines changed

docs/migration.md

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ async def handle_call_tool(ctx: ServerRequestContext, params: CallToolRequestPar
371371
server = Server("my-server", on_call_tool=handle_call_tool)
372372
```
373373

374-
### `RequestContext` and `ProgressContext` type parameters simplified
374+
### `RequestContext` type parameters simplified
375375

376376
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.
377377

@@ -380,40 +380,59 @@ The `RequestContext` class has been split to separate shared fields from server-
380380
- Type parameters reduced from `RequestContext[SessionT, LifespanContextT, RequestT]` to `RequestContext[SessionT]`
381381
- Server-specific fields (`lifespan_context`, `experimental`, `request`, `close_sse_stream`, `close_standalone_sse_stream`) moved to new `ServerRequestContext` class in `mcp.server.context`
382382

383-
**`ProgressContext` changes:**
384-
385-
- Type parameters reduced from `ProgressContext[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT]` to `ProgressContext[SessionT]`
386-
387383
**Before (v1):**
388384

389385
```python
390386
from mcp.client.session import ClientSession
391387
from mcp.shared.context import RequestContext, LifespanContextT, RequestT
392-
from mcp.shared.progress import ProgressContext
393388

394389
# RequestContext with 3 type parameters
395390
ctx: RequestContext[ClientSession, LifespanContextT, RequestT]
396-
397-
# ProgressContext with 5 type parameters
398-
progress_ctx: ProgressContext[SendRequestT, SendNotificationT, SendResultT, ReceiveRequestT, ReceiveNotificationT]
399391
```
400392

401393
**After (v2):**
402394

403395
```python
404396
from mcp.client.context import ClientRequestContext
405-
from mcp.client.session import ClientSession
406397
from mcp.server.context import ServerRequestContext, LifespanContextT, RequestT
407-
from mcp.shared.progress import ProgressContext
408398

409399
# For client-side context (sampling, elicitation, list_roots callbacks)
410400
ctx: ClientRequestContext
411401

412402
# For server-specific context with lifespan and request types
413403
server_ctx: ServerRequestContext[LifespanContextT, RequestT]
404+
```
405+
406+
### `ProgressContext` and `progress()` context manager removed
407+
408+
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.
409+
410+
**Before:**
411+
412+
```python
413+
from mcp.shared.progress import progress
414414

415-
# ProgressContext with 1 type parameter
416-
progress_ctx: ProgressContext[ClientSession]
415+
with progress(ctx, total=100) as p:
416+
await p.progress(25)
417+
```
418+
419+
**After — use `Context.report_progress()` (recommended):**
420+
421+
```python
422+
@server.tool()
423+
async def my_tool(x: int, ctx: Context) -> str:
424+
await ctx.report_progress(25, 100)
425+
return "done"
426+
```
427+
428+
**After — use `session.send_progress_notification()` (low-level):**
429+
430+
```python
431+
await session.send_progress_notification(
432+
progress_token=progress_token,
433+
progress=25,
434+
total=100,
435+
)
417436
```
418437

419438
### Resource URI type changed from `AnyUrl` to `str`

src/mcp/shared/progress.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

tests/shared/test_progress_notifications.py

Lines changed: 0 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
from mcp.server.lowlevel import NotificationOptions
1111
from mcp.server.models import InitializationOptions
1212
from mcp.server.session import ServerSession
13-
from mcp.shared._context import RequestContext
1413
from mcp.shared.message import SessionMessage
15-
from mcp.shared.progress import progress
1614
from mcp.shared.session import RequestResponder
1715

1816

@@ -198,117 +196,6 @@ async def handle_client_message(
198196
assert server_progress_updates[2]["progress"] == 1.0
199197

200198

201-
@pytest.mark.anyio
202-
async def test_progress_context_manager():
203-
"""Test client using progress context manager for sending progress notifications."""
204-
# Create memory streams for client/server
205-
server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[SessionMessage](5)
206-
client_to_server_send, client_to_server_receive = anyio.create_memory_object_stream[SessionMessage](5)
207-
208-
# Track progress updates
209-
server_progress_updates: list[dict[str, Any]] = []
210-
211-
progress_token = None
212-
213-
# Register progress handler
214-
async def handle_progress(ctx: ServerRequestContext, params: types.ProgressNotificationParams) -> None:
215-
server_progress_updates.append(
216-
{
217-
"token": params.progress_token,
218-
"progress": params.progress,
219-
"total": params.total,
220-
"message": params.message,
221-
}
222-
)
223-
224-
server = Server(name="ProgressContextTestServer", on_progress=handle_progress)
225-
226-
# Run server session to receive progress updates
227-
async def run_server():
228-
# Create a server session
229-
async with ServerSession(
230-
client_to_server_receive,
231-
server_to_client_send,
232-
InitializationOptions(
233-
server_name="ProgressContextTestServer",
234-
server_version="0.1.0",
235-
capabilities=server.get_capabilities(NotificationOptions(), {}),
236-
),
237-
) as server_session:
238-
async for message in server_session.incoming_messages:
239-
try:
240-
await server._handle_message(message, server_session, {})
241-
except Exception as e: # pragma: no cover
242-
raise e
243-
244-
# Client message handler
245-
async def handle_client_message(
246-
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
247-
) -> None:
248-
if isinstance(message, Exception): # pragma: no cover
249-
raise message
250-
251-
# run client session
252-
async with (
253-
ClientSession(
254-
server_to_client_receive,
255-
client_to_server_send,
256-
message_handler=handle_client_message,
257-
) as client_session,
258-
anyio.create_task_group() as tg,
259-
):
260-
tg.start_soon(run_server)
261-
262-
await client_session.initialize()
263-
264-
progress_token = "client_token_456"
265-
266-
# Create request context
267-
request_context = RequestContext(
268-
request_id="test-request",
269-
session=client_session,
270-
meta={"progress_token": progress_token},
271-
)
272-
273-
# Utilize progress context manager
274-
with progress(request_context, total=100) as p:
275-
await p.progress(10, message="Loading configuration...")
276-
await p.progress(30, message="Connecting to database...")
277-
await p.progress(40, message="Fetching data...")
278-
await p.progress(20, message="Processing results...")
279-
280-
# Wait for all messages to be processed
281-
await anyio.sleep(0.5)
282-
tg.cancel_scope.cancel()
283-
284-
# Verify progress updates were received by server
285-
assert len(server_progress_updates) == 4
286-
287-
# first update
288-
assert server_progress_updates[0]["token"] == progress_token
289-
assert server_progress_updates[0]["progress"] == 10
290-
assert server_progress_updates[0]["total"] == 100
291-
assert server_progress_updates[0]["message"] == "Loading configuration..."
292-
293-
# second update
294-
assert server_progress_updates[1]["token"] == progress_token
295-
assert server_progress_updates[1]["progress"] == 40
296-
assert server_progress_updates[1]["total"] == 100
297-
assert server_progress_updates[1]["message"] == "Connecting to database..."
298-
299-
# third update
300-
assert server_progress_updates[2]["token"] == progress_token
301-
assert server_progress_updates[2]["progress"] == 80
302-
assert server_progress_updates[2]["total"] == 100
303-
assert server_progress_updates[2]["message"] == "Fetching data..."
304-
305-
# final update
306-
assert server_progress_updates[3]["token"] == progress_token
307-
assert server_progress_updates[3]["progress"] == 100
308-
assert server_progress_updates[3]["total"] == 100
309-
assert server_progress_updates[3]["message"] == "Processing results..."
310-
311-
312199
@pytest.mark.anyio
313200
async def test_progress_callback_exception_logging():
314201
"""Test that exceptions in progress callbacks are logged and \

0 commit comments

Comments
 (0)