From 401272ab7243ff4b1924a047394150574881d17a Mon Sep 17 00:00:00 2001 From: amasen02 Date: Sat, 5 Sep 2026 22:02:57 +0530 Subject: [PATCH] fix(dispatcher): preserve non-canonical numeric strings in coerce_request_id Ensure coerce_request_id only folds canonical integer strings (str(int(s)) == s) so wire-distinct JSON-RPC ids like '007', '+7', '1_000', and ' 7 ' do not collide on a shared correlation key. Fixes #3432. --- src/mcp/shared/dispatcher.py | 4 +++- tests/shared/test_jsonrpc_dispatcher.py | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mcp/shared/dispatcher.py b/src/mcp/shared/dispatcher.py index f2ff96e7d5..7b749f03fe 100644 --- a/src/mcp/shared/dispatcher.py +++ b/src/mcp/shared/dispatcher.py @@ -61,7 +61,9 @@ def coerce_request_id(request_id: RequestId) -> RequestId: """ if isinstance(request_id, str): try: - return int(request_id) + parsed = int(request_id) + if str(parsed) == request_id: + return parsed except ValueError: pass return request_id diff --git a/tests/shared/test_jsonrpc_dispatcher.py b/tests/shared/test_jsonrpc_dispatcher.py index 9bee8b2c3b..04de284a38 100644 --- a/tests/shared/test_jsonrpc_dispatcher.py +++ b/tests/shared/test_jsonrpc_dispatcher.py @@ -2011,6 +2011,13 @@ def test_coerce_request_id_passes_through_non_numeric_string_and_int(): assert coerce_request_id(42) == 42 +def test_coerce_request_id_does_not_fold_non_canonical_numeric_strings(): + for non_canonical in ("007", "+7", "1_000", " 7 ", "٧"): + assert coerce_request_id(non_canonical) == non_canonical + assert coerce_request_id("-3") == -3 + assert coerce_request_id("0") == 0 + + @pytest.mark.anyio async def test_jsonrpc_error_response_with_null_id_is_dropped(): """Parse-error responses (id=null) have no waiter; they're dropped and the read loop stays healthy."""