Skip to content

Commit f38c623

Browse files
committed
fix: reject boolean progress totals
Signed-off-by: Dhruv Maniya <dhruvmaniya1998@gmail.com>
1 parent a4f4ccd commit f38c623

2 files changed

Lines changed: 12 additions & 8 deletions

File tree

src/mcp/shared/jsonrpc_dispatcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ def _dispatch_notification(
639639
self._spawn(
640640
_shielded_progress(pending.on_progress),
641641
float(progress),
642-
float(total) if isinstance(total, int | float) else None,
642+
float(total) if isinstance(total, int | float) and not isinstance(total, bool) else None,
643643
message if isinstance(message, str) else None,
644644
sender_ctx=sender_ctx,
645645
)

tests/shared/test_jsonrpc_dispatcher.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,14 +2173,17 @@ async def call() -> None:
21732173

21742174

21752175
@pytest.mark.anyio
2176-
async def test_progress_with_bool_token_or_bool_progress_does_not_fire_callback():
2177-
"""Bool `progressToken`/`progress` values are malformed; the callback must
2178-
not fire for the unrelated request keyed by id 1 (`True == 1`)."""
2176+
async def test_progress_boolean_fields_are_not_coerced_to_numbers():
2177+
"""Raw wire input is required because the typed API rejects boolean progress fields.
2178+
2179+
Boolean tokens and progress values are ignored, while a boolean optional
2180+
total is treated as absent on an otherwise valid notification.
2181+
"""
21792182
c2s_send, c2s_recv = anyio.create_memory_object_stream[SessionMessage | Exception](32)
21802183
s2c_send, s2c_recv = anyio.create_memory_object_stream[SessionMessage | Exception](32)
21812184
client: JSONRPCDispatcher[TransportContext] = JSONRPCDispatcher(s2c_recv, c2s_send)
21822185
on_request, on_notify = echo_handlers(Recorder())
2183-
seen: list[float] = []
2186+
seen: list[tuple[float, float | None]] = []
21842187
try:
21852188
async with anyio.create_task_group() as tg:
21862189
await tg.start(client.run, on_request, on_notify)
@@ -2194,7 +2197,8 @@ async def respond_with_malformed_then_valid_progress() -> None:
21942197
for params in (
21952198
{"progressToken": True, "progress": 0.1}, # bool token
21962199
{"progressToken": rid, "progress": True}, # bool progress
2197-
{"progressToken": rid, "progress": 0.5}, # valid
2200+
{"progressToken": rid, "progress": 0.5, "total": True}, # bool total
2201+
{"progressToken": rid, "progress": 0.75, "total": 1}, # valid
21982202
):
21992203
await s2c_send.send(
22002204
SessionMessage(
@@ -2208,7 +2212,7 @@ async def respond_with_malformed_then_valid_progress() -> None:
22082212
)
22092213

22102214
async def on_progress(progress: float, total: float | None, message: str | None) -> None:
2211-
seen.append(progress)
2215+
seen.append((progress, total))
22122216

22132217
tg.start_soon(respond_with_malformed_then_valid_progress)
22142218
result = await client.send_raw_request("ping", None, {"on_progress": on_progress})
@@ -2217,7 +2221,7 @@ async def on_progress(progress: float, total: float | None, message: str | None)
22172221
finally:
22182222
for s in (c2s_send, c2s_recv, s2c_send, s2c_recv):
22192223
s.close()
2220-
assert seen == [0.5] # only the well-formed progress fired the callback
2224+
assert seen == [(0.5, None), (0.75, 1.0)]
22212225

22222226

22232227
@pytest.mark.anyio

0 commit comments

Comments
 (0)